// -*- objc -*-
// --------------------------------------------------------------------
// IpePresenter with Cocoa UI
// --------------------------------------------------------------------
/*

    This file is part of the extensible drawing editor Ipe.
    Copyright (c) 1993-2019 Otfried Cheong

    Ipe is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    As a special exception, you have permission to link Ipe with the
    CGAL library and distribute executables, as long as you follow the
    requirements of the Gnu General Public License in regard to all of
    the software in the executable aside from CGAL.

    Ipe is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with Ipe; if not, you can find it at
    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "ipepresenter.h"
#include "ipepdfview_cocoa.h"
#include "ipeselector_cocoa.h"

#import <Cocoa/Cocoa.h>

using ipe::String;

inline String N2I(NSString *aStr) { return String(aStr.UTF8String); }
inline NSString *I2N(String s) {return [NSString stringWithUTF8String:s.z()];}

// --------------------------------------------------------------------

@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate, NSSplitViewDelegate>
@end

@interface IpePdfSelectorProvider : IpeSelectorProvider

@property ipe::PdfFile *pdf;
@property ipe::PdfThumbnail *pdfThumb;
@property NSMutableArray<NSString *> *pdfLabels;

- (int) count;
- (NSString *) title:(int) index;
- (ipe::Buffer) renderImage:(int) index;
@end

// --------------------------------------------------------------------

@implementation IpePdfSelectorProvider

- (int) count
{
  return self.pdf->countPages();
}

- (NSString *) title:(int) index
{
  return self.pdfLabels[index];
}

- (ipe::Buffer) renderImage:(int) index
{
  return self.pdfThumb->render(self.pdf->page(index));
}
@end

// --------------------------------------------------------------------

class AppUi : public Presenter {
public:
  AppUi();
  bool load(NSString *aFname);
  void setPdf();
  void setView();
  void fitBoxAll();
  void setTime();
  void timerElapsed();
  void selectPage();

public:
  int iTime;
  bool iCountDown;
  bool iCountTime;

  NSWindow *iWindow;
  NSWindow *iScreenWindow;

  NSSplitView *iContent;
  NSSplitView *iRightSide;
  NSStackView *iTopRight;

  NSTextField *iClock;
  NSTextView *iNotesView;
  NSScrollView *iNotes;

  IpePdfView *iCurrent;
  IpePdfView *iNext;
  IpePdfView *iScreen;

  IpePdfSelectorProvider *iProvider;
};

AppUi::AppUi() : iTime{0}, iCountDown{false}, iCountTime{false},
  iCurrent{nil}, iNext{nil}, iScreen{nil}, iProvider{nil}
{
  NSRect contentRect = NSMakeRect(335., 390., 800., 600.);
  NSRect mainRect = NSMakeRect(0., 0., 200., 100.);
  NSRect subRect = NSMakeRect(0., 0., 100., 100.);
  NSRect clockRect = NSMakeRect(0., 0., 100., 30.);

  iWindow = [[NSWindow alloc]
	       initWithContentRect:contentRect
			 styleMask:(NSTitledWindowMask|
				    NSClosableWindowMask|
				    NSResizableWindowMask|
				    NSMiniaturizableWindowMask)
			   backing:NSBackingStoreBuffered
			     defer:YES];

  iContent = [[NSSplitView alloc] initWithFrame:subRect];
  iContent.vertical = YES;

  iRightSide = [[NSSplitView alloc] initWithFrame:subRect];
  iRightSide.vertical = NO;

  iCurrent = [[IpePdfView alloc] initWithFrame:mainRect];
  iNext = [[IpePdfView alloc] initWithFrame:subRect];

  iClock = [[NSTextField alloc] initWithFrame:clockRect];
  iClock.bordered= NO;
  iClock.drawsBackground = NO;
  iClock.editable = NO;
  iClock.font = [NSFont labelFontOfSize:24.0];
  iClock.alignment = NSCenterTextAlignment;
  iClock.usesSingleLineMode = YES;

  iNotes = [[NSScrollView alloc] initWithFrame:subRect];
  iNotesView = [[NSTextView alloc] initWithFrame:subRect];
  iNotesView.editable = NO;
  iNotesView.richText = NO;
  [iNotesView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
  [iNotes setDocumentView:iNotesView];
  iNotes.hasVerticalScroller = YES;

  iTopRight = [NSStackView stackViewWithViews:@[iClock, iNotes]];
  iTopRight.orientation = NSUserInterfaceLayoutOrientationVertical;

  [iContent addSubview:iCurrent];
  [iContent addSubview:iRightSide];
  [iRightSide addSubview:iTopRight];
  [iRightSide addSubview:iNext];
  [iContent adjustSubviews];
  [iRightSide adjustSubviews];
  double split = (0.6 * [iRightSide minPossiblePositionOfDividerAtIndex:0] +
		  0.4 * [iRightSide maxPossiblePositionOfDividerAtIndex:0]);
  [iRightSide setPosition:split ofDividerAtIndex:0];

  [iWindow setContentView:iContent];

  iScreenWindow = [[NSWindow alloc]
		    initWithContentRect:contentRect
			      styleMask:(NSTitledWindowMask|
					 NSResizableWindowMask|
					 NSMiniaturizableWindowMask)
				backing:NSBackingStoreBuffered
				  defer:YES];
  iScreen = [[IpePdfView alloc] initWithFrame:subRect];
  [iScreenWindow setContentView:iScreen];
}

bool AppUi::load(NSString *aFname)
{
  bool result = Presenter::load(N2I(aFname).z());
  if (result) {
    setPdf();
    setView();
    fitBoxAll();
    iProvider = nil;
  }
  return result;
}

void AppUi::setPdf()
{
  iCurrent.pdfView->setPdf(iPdf.get());
  iNext.pdfView->setPdf(iPdf.get());
  iScreen.pdfView->setPdf(iPdf.get());
}

void AppUi::setView()
{
  setViewPage(iScreen.pdfView, iPdfPageNo);
  setViewPage(iCurrent.pdfView, iPdfPageNo);
  setViewPage(iNext.pdfView, iPdfPageNo < iPdf->countPages() - 1 ? iPdfPageNo + 1 : iPdfPageNo);

  [iWindow setTitle:I2N(currentLabel())];
  NSAttributedString *n = [[NSAttributedString alloc]
			    initWithString:I2N(iAnnotations[iPdfPageNo])];
  [[iNotesView textStorage] setAttributedString:n];
  iNotesView.textColor = [NSColor textColor];
  iNotesView.font = [NSFont labelFontOfSize:14.0];
  setTime();
}

void AppUi::fitBoxAll()
{
  if (!iPdf)
    return;
  fitBox(mediaBox(-1), iCurrent.pdfView);
  fitBox(mediaBox(-2), iNext.pdfView);
  fitBox(mediaBox(-1), iScreen.pdfView);
}

void AppUi::setTime()
{
  [iClock setStringValue:[NSString stringWithFormat:@"%d:%02d:%02d",
				   iTime / 3600, (iTime / 60) % 60, iTime % 60]];
  [iRightSide adjustSubviews];
}

void AppUi::timerElapsed()
{
  if (iCountTime) {
    if (iCountDown) {
      if (iTime > 0)
	--iTime;
    } else
      ++iTime;
    setTime();
  }
}

void AppUi::selectPage()
{
  constexpr int iconWidth = 250;

  if (!iProvider) {
    iProvider = [IpePdfSelectorProvider new];
    iProvider.pdf = iPdf.get();
    iProvider.pdfThumb = new ipe::PdfThumbnail(iPdf.get(), iconWidth);

    iProvider.pdfLabels = [NSMutableArray new];
    for (int i = 0; i < iPdf->countPages(); ++i)
      [iProvider.pdfLabels addObject:I2N(pageLabel(i))];

    NSSize tnSize;
    tnSize.width = iProvider.pdfThumb->width() / 2.0;
    tnSize.height = iProvider.pdfThumb->height() / 2.0;
    iProvider.tnSize = tnSize;
  }

  const char *title = "IpePresenter: Select page";
  int width = 800;
  int height = 600;

  int sel = showPageSelectDialog(width, height, title, iProvider, iPdfPageNo);
  if (sel >= 0) {
    iPdfPageNo = sel;
    setView();
  }
}

// --------------------------------------------------------------------

static void setItemShortcut(NSMenu *menu, int index, unichar code)
{
  NSMenuItem *item = [menu itemAtIndex:index];
  item.keyEquivalent = [NSString stringWithFormat:@"%C", code];
  item.keyEquivalentModifierMask = 0;
}

// --------------------------------------------------------------------

@implementation AppDelegate  {
  AppUi *ui;
}

- (instancetype) init {
  self = [super init];
  if (self)
    ui = new AppUi();
  return self;
}

- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) app {
  return YES;
}

- (void) applicationWillFinishLaunching:(NSNotification *) notification {
  [ui->iWindow setDelegate:(id<NSWindowDelegate>) self];
  [ui->iScreenWindow setDelegate:(id<NSWindowDelegate>) self];
  [ui->iContent setDelegate:(id<NSSplitViewDelegate>) self];
  [ui->iRightSide setDelegate:(id<NSSplitViewDelegate>) self];
}

- (void) applicationDidFinishLaunching:(NSNotification *) aNotification {
  NSMenu *menu = [NSApp menu];
  int i = [menu indexOfItemWithTag:13];
  NSMenu *navi = [menu itemAtIndex:i].submenu;
  setItemShortcut(navi, 0, NSRightArrowFunctionKey);
  setItemShortcut(navi, 1, NSDownArrowFunctionKey);
  setItemShortcut(navi, 2, NSLeftArrowFunctionKey);
  setItemShortcut(navi, 3, NSUpArrowFunctionKey);

  [ui->iWindow makeKeyAndOrderFront:self];
  ui->fitBoxAll();

  [NSTimer scheduledTimerWithTimeInterval:1.0
				   target:self
				 selector:@selector(timerFired:)
				 userInfo:nil
				  repeats:YES];

  if (ui->iFileName.empty())
    [self openDocument:self];
}

- (BOOL) application:(NSApplication *) app openFile:(NSString *) filename
{
  return ui->load(filename);
}

- (void) windowDidEndLiveResize:(NSNotification *) notification
{
  ui->fitBoxAll();
}

- (void) splitViewDidResizeSubviews:(NSNotification *) notification
{
  ui->fitBoxAll();
}

- (void) windowDidExitFullScreen:(NSNotification *) notification
{
  ui->fitBoxAll();
}

- (BOOL) validateMenuItem:(NSMenuItem *) item
{
  if (item.action == @selector(countDown:)) {
    item.state = ui->iCountDown ? NSOnState : NSOffState;
  } else if (item.action == @selector(countTime:)) {
    item.state = ui->iCountTime ? NSOnState : NSOffState;
  }
  return YES;
}

- (void) pdfViewMouseButton:(NSEvent *) event
{
  int button = event.buttonNumber;
  if (button == 0)
    [self nextView:event.window];
  else if (button == 1)
    [self previousView:event.window];
}

- (BOOL) windowShouldClose:(id) sender
{
  if (sender == ui->iWindow)
    [ui->iScreenWindow close]; // also close presentation window
  return true;
}

- (void) timerFired:(NSTimer *) timer
{
  ui->timerElapsed();
}

// --------------------------------------------------------------------

- (void) openDocument:(id) sender {
  NSOpenPanel *panel = [NSOpenPanel openPanel];
  [panel beginSheetModalForWindow:ui->iWindow completionHandler:
    ^(NSInteger result) {
      if (result == NSFileHandlingPanelOKButton) {
	NSURL *url = [[panel URLs] objectAtIndex:0];
	if ([url isFileURL])
	  ui->load([url path]);
      }
      // canceled or failed to load and we didn't have a document before
      if (ui->iFileName.empty())
	  [NSApp terminate:self];
    }
   ];
}

- (void) showPresentation:(id) sender
{
  [ui->iScreenWindow setIsVisible:true];
}

- (void) setTime:(id) sender
{
  NSString *input = [self input:@"Enter time in minutes:" defaultValue:@""];
  if (input) {
    ipe::Lex lex(N2I(input));
    int minutes = lex.getInt();
    ui->iTime = 60 * minutes;
    ui->setTime();
  }
}

- (void) resetTime:(id) sender
{
  ui->iTime = 0;
  ui->setTime();
}

- (void) countDown:(id) sender
{
  ui->iCountDown = !ui->iCountDown;
}

- (void) countTime:(id) sender
{
  ui->iCountTime = !ui->iCountTime;
}

- (void) nextView:(id) sender
{
  ui->nextView(+1);
  ui->setView();
}

- (void) previousView:(id) sender
{
  ui->nextView(-1);
  ui->setView();
}

- (void) nextPage:(id) sender
{
  ui->nextPage(+1);
  ui->setView();
}

- (void) previousPage:(id) sender
{
  ui->nextPage(-1);
  ui->setView();
}

- (void) jumpTo:(id) sender
{
  NSString *input = [self input:@"Enter page label:" defaultValue:@""];
  if (input) {
    ui->jumpToPage(N2I(input));
    ui->setView();
  }
}

- (void) selectPage:(id) sender
{
  ui->selectPage();
}

- (NSString *) input: (NSString *) prompt defaultValue: (NSString *)defaultValue
{
  NSAlert *alert = [[NSAlert alloc] init];
  alert.messageText = prompt;
  [alert addButtonWithTitle:@"Ok"];
  [alert addButtonWithTitle:@"Cancel"];

  NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
  input.stringValue = defaultValue;
  alert.accessoryView = input;

  NSInteger button = [alert runModal];
  if (button == NSAlertFirstButtonReturn) {
    [input validateEditing];
    return [input stringValue];
  } else
    return nil;
}
@end

// --------------------------------------------------------------------

int main(int argc, const char * argv[])
{
  ipe::Platform::initLib(ipe::IPELIB_VERSION);
  return NSApplicationMain(argc, argv);
}

// --------------------------------------------------------------------
