/*
 *  ppui/DialogBase.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker 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.
 *
 *  Milkytracker 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 Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 *  DialogBase.cpp
 *  MilkyTracker
 *
 *  Created by Peter Barth on 06.10.05.
 *
 */

#include "DialogBase.h"
#include "Screen.h"
#include "StaticText.h"
#include "MessageBoxContainer.h"
#include "Font.h"
#include "SimpleVector.h"

// parse string and create a string vector with substrings
// all fitting into width when drawn with font f
static void wrapString(PPSimpleVector<PPString>& stringParts, PPFont& f, const PPString& str, pp_int32 width)
{
	if (f.getStrWidth(str) <= (unsigned)width)
	{ 
		stringParts.add(new PPString(str));		
		return;
	}
	
	PPString strCopy = str;
	
	stringParts.clear();
	
	while (strCopy.length() > 0)
	{
		pp_int32 i = 0;
		pp_int32 j = -1;
		do
		{
			if (i < (signed)strCopy.length() && 
			    strCopy.charAt(i) == ' ')
				j = i;
				
			i++;
		} while (i <= (signed)strCopy.length() && 
			     (signed)f.getStrWidth(strCopy.subString(0,i)) <= width);
		i--;
		
		if (i != (signed)strCopy.length() && j >= 0)
			i = j+1;
		
		stringParts.add(new PPString(strCopy.subString(0,i)));		
		strCopy = strCopy.subString(i,strCopy.length());			
	}
}

void PPDialogBase::initCommon(PPScreen* screen, 
								   DialogResponder* responder,
								   pp_int32 id, 
								   const PPString& caption, 
								   pp_int32 width, 
								   pp_int32 height,
								   pp_int32& x, pp_int32& y)
{
	respondListener = responder;
	parentScreen = screen;
	messageBoxContainerGeneric = NULL;

/*#ifdef __LOWRES__
	height+=15;
#endif*/
	x = screen->getWidth() / 2 - width/2;
	y = screen->getHeight() / 2 - height/2;

	messageBoxContainerGeneric = new PPMessageBoxContainer(id, screen, this, PPPoint(x, y), PPSize(width,height), "System request");
	messageBoxContainerGeneric->show(false);

	pp_int32 x2 = x + width / 2 - (PPFont::getFont(PPFont::FONT_SYSTEM)->getStrWidth(caption) / 2);
	pp_int32 y2 = y + 28;

	messageBoxContainerGeneric->addControl(new PPStaticText(MESSAGEBOX_STATICTEXT_MAIN_CAPTION, screen, this, PPPoint(x2, y2), caption, true));	
}	

void PPDialogBase::initDialog(PPScreen* screen, 
							  DialogResponder* responder,
							  pp_int32 id,
							  const PPString& caption,
							  const PPString& message,
							  pp_int32 captionOffset,
							  const PPString& buttonYesCaption)
{
	respondListener = responder;
	parentScreen = screen;
	messageBoxContainerGeneric = NULL;

	pp_uint32 width = 290;
	
	PPFont* font = PPFont::getFont(PPFont::FONT_SYSTEM);
	
	PPSimpleVector<PPString> stringList;
	wrapString(stringList, *font, message, width - (font->getCharWidth()*2 + 4));

	pp_uint32 height = 62 + stringList.size()*12;

/*#ifdef __LOWRES__
	height+=15;
#endif*/
	pp_int32 x = screen->getWidth() / 2 - width/2;
	pp_int32 y = screen->getHeight() / 2 - height/2;

	messageBoxContainerGeneric = new PPMessageBoxContainer(id, screen, this, PPPoint(x, y), PPSize(width,height), caption);
	messageBoxContainerGeneric->show(false);

	pp_int32 y2 = y + 28;

	for (pp_int32 i = 0; i < stringList.size(); i++)
	{
		pp_int32 x2 = x + width / 2 - (font->getStrWidth(*stringList.get(i)) / 2);
		
		messageBoxContainerGeneric->addControl(new PPStaticText(MESSAGEBOX_STATICTEXT_MAIN_CAPTION+i, screen, this, PPPoint(x2, y2), *stringList.get(i), true));
		y2+=12;
	}

/*#ifdef __LOWRES__
	pp_int32 y3 = y + height - (26 + 15);
#else
	pp_int32 y3 = y + height - 26;
#endif*/
	pp_int32 y3 = y + height - captionOffset;

	PPButton* button;

	button = new PPButton(PP_MESSAGEBOX_BUTTON_YES, screen, this, PPPoint(x+width/2 - 30, y3), PPSize(60, 11));
	button->setText(buttonYesCaption);
	messageBoxContainerGeneric->addControl(button);
}

void PPDialogBase::initDialog(PPScreen* screen, 
											  DialogResponder* responder,
											  pp_int32 id,
											  const PPString& caption,
											  pp_int32 width,
											  pp_int32 height,
											  pp_int32 captionOffset,
											  const PPString& buttonYesCaption) 
{
	pp_int32 x,y;
	initCommon(screen, responder, id, caption, width, height, x, y);

/*#ifdef __LOWRES__
	pp_int32 y3 = y + height - (26 + 15);
#else
	pp_int32 y3 = y + height - 26;
#endif*/
	pp_int32 y3 = y + height - captionOffset;

	PPButton* button;

	button = new PPButton(PP_MESSAGEBOX_BUTTON_YES, screen, this, PPPoint(x+width/2-30, y3), PPSize(60, 11));
	button->setText(buttonYesCaption);
	messageBoxContainerGeneric->addControl(button);
}

void PPDialogBase::initDialog(PPScreen* screen, 
											  DialogResponder* responder,
											  pp_int32 id,
											  const PPString& caption,
											  pp_int32 width,
											  pp_int32 height,
											  pp_int32 captionOffset,
											  const PPString& buttonYesCaption,
											  const PPString& buttonCancelCaption) 
{
	pp_int32 x,y;
	initCommon(screen, responder, id, caption, width, height, x, y);

/*#ifdef __LOWRES__
	pp_int32 y3 = y + height - (26 + 15);
#else
	pp_int32 y3 = y + height - 26;
#endif*/
	pp_int32 y3 = y + height - captionOffset;

	PPButton* button;

	button = new PPButton(PP_MESSAGEBOX_BUTTON_YES, screen, this, PPPoint(x+width/2-65, y3), PPSize(60, 11));
	button->setText(buttonYesCaption);
	messageBoxContainerGeneric->addControl(button);
	
	button = new PPButton(PP_MESSAGEBOX_BUTTON_CANCEL, screen, this, PPPoint(x+width/2+5, y3), PPSize(60, 11));
	button->setText(buttonCancelCaption);
	messageBoxContainerGeneric->addControl(button);
}

void PPDialogBase::initDialog(PPScreen* screen, 
											  DialogResponder* responder,
											  pp_int32 id,
											  const PPString& caption,
											  pp_int32 width,
											  pp_int32 height,
											  pp_int32 captionOffset,
											  const PPString& buttonYesCaption,
											  const PPString& buttonNoCaption,
											  const PPString& buttonCancelCaption) 
{
	pp_int32 x,y;
	initCommon(screen, responder, id, caption, width, height, x, y);

/*#ifdef __LOWRES__
	pp_int32 y3 = y + height - (26 + 15);
#else
	pp_int32 y3 = y + height - 26;
#endif*/
	pp_int32 y3 = y + height - captionOffset;

	PPButton* button;

	button = new PPButton(PP_MESSAGEBOX_BUTTON_YES, screen, this, PPPoint(x+width/2-100, y3), PPSize(60, 11));
	button->setText(buttonYesCaption);
	messageBoxContainerGeneric->addControl(button);
	
	button = new PPButton(PP_MESSAGEBOX_BUTTON_NO, screen, this, PPPoint(x+width/2-30, y3), PPSize(60, 11));
	button->setText(buttonNoCaption);
	messageBoxContainerGeneric->addControl(button);
	
	button = new PPButton(PP_MESSAGEBOX_BUTTON_CANCEL, screen, this, PPPoint(x+width/2+40, y3), PPSize(60, 11));
	button->setText(buttonCancelCaption);
	messageBoxContainerGeneric->addControl(button);
}

PPDialogBase::PPDialogBase(PPScreen* screen, 
						   DialogResponder* responder,
						   pp_int32 id, 
						   const PPString& caption,
						   MessageBoxTypes type/* = MessageBox_OKCANCEL*/) :
	keyDownInvokeKeyCode(-1)
{
	switch (type)
	{
		case MessageBox_OK:
			initDialog(screen, responder, id, "System Request", caption, 26, "Okay");
			break;
		case MessageBox_OKCANCEL:
			initDialog(screen, responder, id, caption, 290, 74, 26, "Yes", "No");
			break;
		case MessageBox_YESNOCANCEL:
			initDialog(screen, responder, id, caption, 290, 74, 26, "Yes", "No", "Cancel");
			break;
	}
}

PPDialogBase::PPDialogBase(PPScreen* screen, 
						   DialogResponder* responder,
						   pp_int32 id, 
						   const PPString& caption,
						   const PPString& message) :
	keyDownInvokeKeyCode(-1)
{
	initDialog(screen, responder, id, caption, message, 26, "Okay");
}

PPDialogBase::~PPDialogBase()
{
	delete messageBoxContainerGeneric;
}

void PPDialogBase::show(bool b/* = true*/)
{
	if (b)
		parentScreen->setModalControl(messageBoxContainerGeneric);
	else
		parentScreen->setModalControl(NULL);
}

void PPDialogBase::sendKey(EEventDescriptor event, pp_uint16 vk, pp_uint16 sc, pp_uint16 chr)
{
	if (event == eKeyDown || event == eKeyUp)
	{
		pp_uint16 vksc[3] = {vk, sc, chr};

		PPEvent keyDown(event, &vksc, sizeof(vksc));

		PPControl* ctrl = messageBoxContainerGeneric->getFocusedControl();
		if (ctrl && ctrl->isListBox())
			ctrl->dispatchEvent(&keyDown);
	}
	else if (event == eKeyChar)
	{
		pp_uint16 vksc[2] = {vk, sc};

		PPEvent keyDown(event, &vksc, sizeof(vksc));

		PPControl* ctrl = messageBoxContainerGeneric->getFocusedControl();
		if (ctrl && ctrl->isListBox())
			ctrl->dispatchEvent(&keyDown);
	}
}

#define CALLHANDLER(HANDLERFUNC) \
	if (event->getID() != eCommand) \
		break; \
	if (respondListener && respondListener->HANDLERFUNC(reinterpret_cast<PPObject*>(this)) == 0) \
		show(false); \
	else if (respondListener == NULL) \
		show(false);

pp_int32 PPDialogBase::handleEvent(PPObject* sender, PPEvent* event)
{
#ifdef __LOWRES__
	static const pp_uint16 scanCodesNumbers[] = {SC_1, SC_2, SC_3, SC_4, SC_5, SC_6, SC_7, SC_8, SC_9, SC_0, SC_PLUS, SC_MINUS, SC_PERIOD, 0, 0, 0, 0};
	static const pp_uint16 asciiCodesNumbers[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', '.', VK_LEFT, VK_RIGHT, VK_DELETE, VK_BACK};
#endif

	if (event->getID() == eKeyDown && keyDownInvokeKeyCode > 0 && *(((pp_int16*)event->getDataPtr())) == keyDownInvokeKeyCode)
	{
		event->cancel();
		return 0;
	}
	else if (event->getID() == eKeyChar && keyDownInvokeKeyCode > 0)
	{
		event->cancel();
	}
	else if (event->getID() == eKeyUp && keyDownInvokeKeyCode > 0 && *(((pp_int16*)event->getDataPtr())) == keyDownInvokeKeyCode)
	{
		keyDownInvokeKeyCode = -1;
		event->cancel();
	}

	if (event->getID() == eCommand || event->getID() == eCommandRepeat)
	{
		switch (reinterpret_cast<PPControl*>(sender)->getID())
		{
			case PP_MESSAGEBOX_BUTTON_YES:
			{
				CALLHANDLER(ActionOkay);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_CANCEL:
			{
				CALLHANDLER(ActionCancel);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_NO:
			{
				CALLHANDLER(ActionNo);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_USER1:
			{
				CALLHANDLER(ActionUser1);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_USER2:
			{
				CALLHANDLER(ActionUser2);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_USER3:
			{
				CALLHANDLER(ActionUser3);
				break;
			}

			case PP_MESSAGEBOX_BUTTON_USER4:
			{
				CALLHANDLER(ActionUser4);
				break;
			}
						
			// Input keys
#ifdef __LOWRES__
			case MESSAGEBOX_BUTTON_KEYS_BASE:	// 1
			case MESSAGEBOX_BUTTON_KEYS_BASE+1:	// 2
			case MESSAGEBOX_BUTTON_KEYS_BASE+2: // 3
			case MESSAGEBOX_BUTTON_KEYS_BASE+3: // 4
			case MESSAGEBOX_BUTTON_KEYS_BASE+4: // 5
			case MESSAGEBOX_BUTTON_KEYS_BASE+5: // 6
			case MESSAGEBOX_BUTTON_KEYS_BASE+6: // 7
			case MESSAGEBOX_BUTTON_KEYS_BASE+7: // 8
			case MESSAGEBOX_BUTTON_KEYS_BASE+8: // 9
			case MESSAGEBOX_BUTTON_KEYS_BASE+9: // 0
			case MESSAGEBOX_BUTTON_KEYS_BASE+10: // +
			case MESSAGEBOX_BUTTON_KEYS_BASE+11: // -
			case MESSAGEBOX_BUTTON_KEYS_BASE+12: // .
			{
				pp_int32 i = (reinterpret_cast<PPControl*>(sender)->getID() - MESSAGEBOX_BUTTON_KEYS_BASE);
				pp_uint16 key[3];
				key[0] = key[2] = (pp_uint16)asciiCodesNumbers[i];
				key[1] = scanCodesNumbers[i];
				sendKey(eKeyDown, key[0], key[1], key[2]);
				
				sendKey(eKeyChar, key[0], key[1], 0);
				sendKey(eKeyUp, key[0], key[1], key[2]);
				break;
			}
			
			case MESSAGEBOX_BUTTON_KEYS_BASE+13: // LEFT
			case MESSAGEBOX_BUTTON_KEYS_BASE+14: // RIGHT
			case MESSAGEBOX_BUTTON_KEYS_BASE+15: // DEL
			case MESSAGEBOX_BUTTON_KEYS_BASE+16: // BACK
			{
				pp_int32 i = (reinterpret_cast<PPControl*>(sender)->getID() - MESSAGEBOX_BUTTON_KEYS_BASE);
				pp_uint16 key[3];
				key[0] = (pp_uint16)asciiCodesNumbers[i];
				key[1] = scanCodesNumbers[i];
				key[2] = 0;
				sendKey(eKeyDown, key[0], key[1], key[2]);				
				sendKey(eKeyUp, key[0], key[1], key[2]);
				break;
			}
#endif
		}
	}
	
	return 0;
}

void PPDialogBase::setUserButtonText(pp_int32 index, const PPString& caption)
{
	PPButton* button = static_cast<PPButton*>(messageBoxContainerGeneric->getControlByID(PP_MESSAGEBOX_BUTTON_USER1+index));
	if (button)
		button->setText(caption);
}


pp_int32 PPDialogBase::getID() const
{
	if (messageBoxContainerGeneric)
		return messageBoxContainerGeneric->getID();
		
	return 0;
}


void PPDialogBase::setMessageBoxContainer(PPMessageBoxContainer* container)
{
	delete messageBoxContainerGeneric;
	messageBoxContainerGeneric = container;
}


