/*  
	BeOS Front-end du PDF file reader xpdf.
    Copyright (C) 1998 Hubert Figuiere
	Copyright (C) 2000 Michael Pfeiffer

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//
// PDFWindow.cpp
//
// (c) 1998-99 Hubert Figuiere
//
// $Id: PDFWindow.cpp,v 1.8 1999/06/09 21:34:21 hub Exp $
//

#include <stdlib.h>
#include <stdio.h>

#include <interface/Bitmap.h>
#include <interface/View.h>
#include <interface/Screen.h>
#include <interface/MenuBar.h>
#include <interface/MenuItem.h>
#include <interface/MenuField.h>
#include <interface/Button.h>
#include <interface/ScrollView.h>
#include <interface/ScrollBar.h>
#include <interface/StringView.h>
#include <storage/Path.h>
#include <SupportDefs.h>

#include "PDFWindow.h"
#include "PDFView.h"
#include "PDFExceptions.h"
#include "BepdfApplication.h"
#include "FindTextWindow.h"

static rgb_color ltGray = { 238, 238, 238, 0 };

char * PDFWindow::PAGE_MSG_LABEL = "page";

///////////////////////////////////////////////////////////
/*
	Check errors that may happen
*/
PDFWindow::PDFWindow(entry_ref * ref, BRect frame)
	: HWindow(frame, "PDF", B_DOCUMENT_WINDOW, 0)
{
	char pageStr [64];
	mMainView = NULL;
	mFindInProgress = false;
	// mIsZoomed = false;
	
#ifdef DEBUG
	fprintf (stderr, "xpdf: PDFWindow::PDFWindow\n");
#endif
	mPrintSettings = NULL;

	SetUpViews(ref);

	if (mMainView != NULL) {
		mMainView->SetPage (1);
#if __INTEL__
		snprintf (pageStr, sizeof (pageStr), "%d", 1);
#else
		sprintf (pageStr, "%d", 1);
#endif
		mPageNumberItem->SetText (pageStr);
	}
	else {
		// be_app->PostMessage (B_QUIT_REQUESTED);
		return;
	}
}



///////////////////////////////////////////////////////////
PDFWindow::~PDFWindow()
{
}

///////////////////////////////////////////////////////////
bool PDFWindow::LoadFile(entry_ref *ref) {
	if ((mMainView != NULL) && mMainView->LoadFile(ref)) {
		char label[20];
#if __INTEL__
		snprintf (label, sizeof (label), "of %d", mMainView->GetNumPages());
#else
		sprintf (label, "of %d", mMainView->GetNumPages());
#endif
		if (Lock()) {
			mTotalPageNumberItem->SetText (label);
			Unlock();
		}
		return true;
	}
	return false;
}

///////////////////////////////////////////////////////////
void PDFWindow::HandleCommand ( int32 cmd, BMessage * msg )
{		
	int page;
	
	switch ( cmd ) {
	case OPEN_FILE_CMD:
		my_app->OpenFilePanel();
		break;
	case CLOSE_FILE_CMD:
		PostMessage (B_QUIT_REQUESTED );
		break;
	case PAGESETUP_FILE_CMD:
		mMainView->PageSetup ();
		break;
	case PRINT_FILE_CMD:
		mMainView->Print ();
		break;
	case QUIT_APP_CMD:
		be_app->PostMessage (B_QUIT_REQUESTED);
		break;
	case ABOUT_APP_CMD:
		be_app->PostMessage (B_ABOUT_REQUESTED);
		break;
	case FIRST_PAGE_CMD:
		mMainView->SetPage (1);
		break;
	case PREVIOUS_PAGE_CMD:
		page = mMainView->Page();
		mMainView->SetPage (page - 1);
		break;
	case NEXT_PAGE_CMD:
		page = mMainView->Page();
		mMainView->SetPage (page + 1);
		break;
	case LAST_PAGE_CMD:
		mMainView->SetPage (mMainView->GetNumPages());
		break;
	case GOTO_PAGE_CMD:
		{
			status_t err;
			BTextControl * control;
			BControl * ptr;
						
			err  = msg->FindPointer ("source", (void **)&ptr);
			control = dynamic_cast <BTextControl *> (ptr);
			if (control != NULL) {
				const char *txt = control->Text ();
				page = atoi (txt);
				mMainView->SetPage (page);
				mMainView->MakeFocus();
			}
			else {
				/* ERROR */
			}
		}
		break;
	case SET_ZOOM_VALUE_CMD:
		{
			status_t err;
			BMenuItem * item;
			BMenu * menu;
			BArchivable * ptr;
			int32 idx;
						
			err  = msg->FindPointer ("source", (void **)&ptr);
			item = dynamic_cast <BMenuItem *> (ptr);
			if (item != NULL) {
				menu = item->Menu ();
				if (menu == NULL) {
					// ERROR
				}
				else {
					idx = menu->IndexOf (item);
					if (idx > MAX_ZOOM - MIN_ZOOM  + 1) {
						idx = MAX_ZOOM - MIN_ZOOM + 1;
					}
					mMainView->SetZoom (idx + MIN_ZOOM);
				}
			}
		}
		break;
	case SET_ROTATE_VALUE_CMD:
		{
			status_t err;
			BMenuItem * item;
			BMenu * menu;
			BArchivable * ptr;
			int32 idx;
						
			err  = msg->FindPointer ("source", (void **)&ptr);
			item = dynamic_cast <BMenuItem *> (ptr);
			if (item != NULL) {
				menu = item->Menu ();
				if (menu == NULL) {
					// ERROR
				}
				else {
					idx = menu->IndexOf (item);
					mMainView->SetRotation (idx * 90);
				}
			}
		}
		break;

		case HISTORY_BACK_CMD:
			mMainView->Back ();
			break;

		case FIND_CMD:
			Lock();
			mFindWindow = new FindTextWindow(mFindText.String(), Frame(), this);
			Unlock();
			mFindWindow->Show();
			break;
	}
}



///////////////////////////////////////////////////////////
bool PDFWindow::CanClose()
{
	return true;
}


///////////////////////////////////////////////////////////
void PDFWindow::SetUpViews(entry_ref * ref)
{
	float winWidth, winHeight;
	float width, height;
	float screenW, screenH;
	float bottom;
	float stringHeight;
	BScrollView * aScrollView;
	BScreen currentScreen (this);
	BScrollBar * scroller;
	BRect rect;
	char pageStr [16];
	char label [1024];
	int numPages, i;
	BMenuItem * mItem;

	BMenuBar * mBar = new BMenuBar(BRect(0,0,1,18), "mainBar");
	AddChild(mBar);
		
		BMenu * menu = new BMenu("File");
		mBar->AddItem(menu);
			
			ADD_ITEM (menu, "Open…", 'O', MakeCommandMessage( OPEN_FILE_CMD ));
			ADD_SITEM (menu );
			ADD_ITEM (menu, "Page Setup…", 0, MakeCommandMessage (PAGESETUP_FILE_CMD));
			ADD_ITEM (menu, "Print…", 'P', MakeCommandMessage (PRINT_FILE_CMD));
			ADD_SITEM (menu );

			ADD_ITEM (menu, "About PDFViewer…", 0, MakeCommandMessage( ABOUT_APP_CMD ) );
			ADD_SITEM (menu );
			ADD_ITEM (menu, "Close", 'W', MakeCommandMessage( CLOSE_FILE_CMD ) );
//			ADD_ITEM ( menu, "Quit", 'Q' , MakeCommandMessage( QUIT_APP_CMD ));

		menu = new BMenu ( "Edit" );
		mBar->AddItem ( menu );
			ADD_ITEM (menu, "Find…", 'F', MakeCommandMessage( FIND_CMD));
		//	ADD_ITEM (menu, "Find Next", 'N', MakeCommandMessage( FIND_NEXT_CMD));
						
		menu = new BMenu ( "Zoom" );
		mBar->AddItem ( menu );
		menu->SetRadioMode (true);

			ADD_ITEM (menu, "40%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "49%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "58%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "69%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "83%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));

			mItem = new BMenuItem("100%", MakeCommandMessage (SET_ZOOM_VALUE_CMD), 
			                               0);
			mItem->SetMarked(true);
			menu->AddItem(mItem);

			ADD_ITEM (menu, "119%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "144%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "172%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "206%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));
			ADD_ITEM (menu, "249%", 0, MakeCommandMessage (SET_ZOOM_VALUE_CMD));

		menu = new BMenu ("Page");
		mBar->AddItem (menu);
			
			ADD_ITEM (menu, "Next", '+', MakeCommandMessage (NEXT_PAGE_CMD));
			ADD_ITEM (menu, "Previous", '-', MakeCommandMessage (PREVIOUS_PAGE_CMD));	
		menu = new BMenu ("Rotation");
		mBar->AddItem(menu);
		menu->SetRadioMode(true);

			ADD_ITEM (menu, "0°", 0, MakeCommandMessage (SET_ROTATE_VALUE_CMD));
			ADD_ITEM (menu, "90°", 0, MakeCommandMessage (SET_ROTATE_VALUE_CMD));
			ADD_ITEM (menu, "180°", 0, MakeCommandMessage (SET_ROTATE_VALUE_CMD));
			ADD_ITEM (menu, "270°", 0, MakeCommandMessage (SET_ROTATE_VALUE_CMD));
		

	BView * toolBar = new BView (BRect(0, 19.0, Bounds().right, TOOLBAR_BOTTOM), "toolbar", 
								B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, 
								B_WILL_DRAW | B_FRAME_EVENTS );
	toolBar->SetViewColor (ltGray);
	AddChild (toolBar);

	/* Add tool bar gadgets */

	const int buttonSize = TOOLBAR_HEIGHT - 2 * TOOLBUTTON_OFFSET;
	// to first page
	BButton * button = new BButton (BRect (TOOLBUTTON_OFFSET, TOOLBUTTON_OFFSET, 
	                                       TOOLBUTTON_OFFSET + buttonSize, 
	                                       TOOLBAR_HEIGHT - TOOLBUTTON_OFFSET), 
	                                "first_btn", "|<", 
	                                MakeCommandMessage (FIRST_PAGE_CMD));
	toolBar->AddChild (button);
	// to previous page
	button = new BButton (BRect (button->Frame().right + 3, TOOLBUTTON_OFFSET, 
	                                       button->Frame().right + 3 + buttonSize, 
	                                       TOOLBAR_HEIGHT - TOOLBUTTON_OFFSET), 
	                                "prev_btn", "<", 
	                                MakeCommandMessage (PREVIOUS_PAGE_CMD));
	toolBar->AddChild (button);

	// go to page number
	stringHeight = (TOOLBAR_HEIGHT - 12) / 2;
	mPageNumberItem	= new BTextControl (BRect (button->Frame().right + 3, stringHeight, 
	                                          button->Frame().right + 3 + 35, stringHeight + 12), 
	                                          "goto_page",
	                                          "", "", MakeCommandMessage (GOTO_PAGE_CMD));
	mPageNumberItem->SetDivider (0.0);
	mPageNumberItem->SetTarget (this);
	toolBar->AddChild (mPageNumberItem);
	// display total number of pages
	mTotalPageNumberItem = new BStringView (BRect (mPageNumberItem->Frame().right + 3, stringHeight + 4, 
	                                          mPageNumberItem->Frame().right + 3 + 45, stringHeight + 16), 
	                                          "total_num_of_pages", "");
	toolBar->AddChild (mTotalPageNumberItem);
	// to next page
	button = new BButton (BRect (mTotalPageNumberItem->Frame().right + 3, TOOLBUTTON_OFFSET, 
	                             mTotalPageNumberItem->Frame().right + 3 + buttonSize, 
	                             TOOLBAR_HEIGHT - TOOLBUTTON_OFFSET), "next_btn", ">", 
	                                MakeCommandMessage (NEXT_PAGE_CMD));
	toolBar->AddChild (button);	
	// to last page
	button = new BButton (BRect (button->Frame().right + 3, TOOLBUTTON_OFFSET, 
	                                       button->Frame().right + 3 + buttonSize, 
	                                       TOOLBAR_HEIGHT - TOOLBUTTON_OFFSET), 
	                                "last_btn", ">|", 
	                                MakeCommandMessage (LAST_PAGE_CMD));
	toolBar->AddChild (button);	
	// go to previous page in the history log 
	BRect rect2(0, 0, 50, TOOLBUTTON_OFFSET);
	rect2.OffsetTo(button->Frame().right + 3, button->Frame().top);
	button = new BButton (rect2, "back_btn", "Back", MakeCommandMessage (HISTORY_BACK_CMD));
	toolBar->AddChild (button);	
	// Status Text
	mStatusText = new BStringView (BRect (button->Frame().right + 3, stringHeight + 4, 
	                                          button->Frame().right + 3 + 800, stringHeight + 16), 
	                                          "status_text", "Status");
	toolBar->AddChild (mStatusText);

	
	/* end of toolbar gadgets */

	mMainView = new PDFView(ref, BRect(0, toolBar->Frame().bottom+1, Bounds().right - 14, 
				Bounds().bottom - 14), "mainView",
				B_FOLLOW_ALL, B_WILL_DRAW | B_NAVIGABLE);
	if (!mMainView->IsOk()) {
		fprintf (stderr, "mMainView == NULL\n");
		delete mMainView;
		mMainView = NULL;
		return;			//ERROR !
	}
	aScrollView = new BScrollView ("scrollView", mMainView, B_FOLLOW_ALL, 
	                               0, true, true, B_FANCY_BORDER );
	
	AddChild (aScrollView);
	scroller = aScrollView->ScrollBar (B_HORIZONTAL);
	rect = scroller->Bounds();
/*
	scroller->ResizeBy (-PAGE_POPUP_WIDTH, 0.0);	
	scroller->MoveBy (PAGE_POPUP_WIDTH, 0.0);
	mPagePopup = new BPopUpMenu ("page", true, true);
*/	
	numPages = mMainView->GetNumPages();

#if __INTEL__
	snprintf (label, sizeof (label), "of %d", numPages);
#else
	sprintf (label, "of %d", numPages);
#endif
	mTotalPageNumberItem->SetText (label);
/*	
	for (i = 1; i <= numPages; i++) {
#if __INTEL__
		snprintf (pageStr, sizeof (pageStr), "%d", i);
#else
		sprintf (pageStr, "%d", i);
#endif
		ADD_ITEM (mPagePopup, pageStr, 0, MakeCommandMessage (GOTO_PAGE_MENU_CMD));
	}
	bottom = Bounds().bottom;

	BMenuBar * aMenuField = new BMenuBar(BRect (0, bottom - SCROLLBAR_SIZE +, 
	                                      PAGE_POPUP_WIDTH - 1, bottom), "page_menu",
	                                      B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_ITEMS_IN_COLUMN,
	                                      false);
	aMenuField->SetFont (be_plain_font);
	AddChild (aMenuField);
	aMenuField->SetBorder (B_BORDER_EACH_ITEM);
	aMenuField->AddItem (mPagePopup);
*/
	width = mMainView->GetPageWidth(1);
	height = mMainView->GetPageHeight(1);
	
	SetZoomSize (width, height);
	
	screenW = currentScreen.Frame().right - currentScreen.Frame().left;
	screenH = currentScreen.Frame().bottom - currentScreen.Frame().top;
	winWidth = (width > screenW - 64 ? screenW - 64 : width);
	winHeight = (height > screenH - 64 ? screenH - 64 : height);
	ResizeTo (winWidth, winHeight);
	mMainView->MakeFocus ();
}


///////////////////////////////////////////////////////////
void PDFWindow::SetStatus(const char *text) {
	mStatusText->SetText(text);
}
///////////////////////////////////////////////////////////
void PDFWindow::FrameResized (float width, float height)
{
	mMainView->FixScrollbars ();
}

///////////////////////////////////////////////////////////
void 
PDFWindow::SetZoomSize (float w, float h)
{
float width = w + B_V_SCROLL_BAR_WIDTH,
	height = h + TOOLBAR_BOTTOM + B_H_SCROLL_BAR_HEIGHT;
	
	SetSizeLimits (100, width, 100, height);
	ResizeTo(width, height);
	BRect frame(Frame());
	BScreen screen;
	BRect fMax(screen.Frame());
	if (fMax.Height() == 0) return;
	frame.right = frame.left + width;
	frame.bottom = frame.top + height;
	if (frame.right > fMax.right) frame.right = fMax.right;
	if (frame.bottom > fMax.bottom) frame.bottom = fMax.bottom;
	ResizeTo(frame.Width(), frame.Height());
/*
	BRect frame(Frame());
	if (mIsZoomed) {
		BScreen screen;
		BRect fMax(screen.Frame());
		if (fMax.Height() == 0) return;
		frame.right = frame.left + width;
		frame.bottom = frame.top + height;
		if (frame.right > fMax.right) frame.right = fMax.right;
		if (frame.bottom > fMax.bottom) frame.bottom = fMax.bottom;
		ResizeTo(frame.Width(), frame.Height());
	} else {
*/
/*
		width = min_c(width, frame.Width());
		height = min_c(height, frame.Height());
		ResizeTo(width, height);
*/
//	}
}

///////////////////////////////////////////////////////////
/*
void 
PDFWindow::Zoom (BPoint origin, float width, float height)
{
	HWindow::Zoom(origin, width, height);
	BScreen screen;
	BRect frame(screen.Frame());
	if (frame.Width() == 0) return;
	float minWidth, maxWidth, minHeight, maxHeight;
	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
	mIsZoomed = ((frame.right == Frame().right) ||
		(width == maxWidth))
		&&
		((frame.bottom == Frame().bottom) ||
		(height == maxHeight));

	printf("(%f, %f), (%f %f), (%f, %f), (%f, %f)\n",
		Frame().right, Frame().bottom,
		frame.right, frame.bottom,
		maxWidth, maxHeight,
		width, height);
	fflush(stdout);
}
*/

///////////////////////////////////////////////////////////
void 
PDFWindow::MessageReceived (BMessage * message)
{
	char pageStr [64];
	int32 page;
	status_t err;
	const char *findText;

	switch (message->what) {
	case PAGE_CHANGE_NOTIFY_MSG:
		err = message->FindInt32 (PAGE_MSG_LABEL, &page);
		if (err == B_NO_ERROR) {
#if __INTEL__
			snprintf (pageStr, sizeof (pageStr), "%d", page);
#else
			sprintf (pageStr, "%d", page);
#endif
			mPageNumberItem->SetText (pageStr);
		}
		break;
	case FindTextWindow::FIND_START_NOTIFY_MSG:
		mFindInProgress = true;
		mFindState = (uint32) FindTextWindow::FIND_STOP_NOTIFY_MSG;
		message->FindString("text", &findText);
		mFindText.SetTo(findText);
		mMainView->Find(findText, this, mFindWindow);
		break;
	case FindTextWindow::FIND_STOP_NOTIFY_MSG:
	case FindTextWindow::FIND_ABORT_NOTIFY_MSG:
		if (mFindInProgress) {
			mFindState = message->what;
			mMainView->StopFind();
		} else {
			mFindWindow->PostMessage(message->what);			
			if (message->what == (uint32)FindTextWindow::FIND_ABORT_NOTIFY_MSG) {
				mFindWindow->Quit();
			}
		}
		break;
	case FindTextWindow::TEXT_FOUND_NOTIFY_MSG:
	case FindTextWindow::TEXT_NOT_FOUND_NOTIFY_MSG:
		mFindInProgress = false;		
		mFindWindow->PostMessage(mFindState);
		if (mFindState == (uint32)FindTextWindow::FIND_ABORT_NOTIFY_MSG) {
			mFindWindow->Quit();
		}
		break;
	default:	
		HWindow::MessageReceived (message);
	}
}


///////////////////////////////////////////////////////////
