/*
** imagescroller.cpp
** Scrolls through multiple fullscreen image pages,
**
**---------------------------------------------------------------------------
** Copyright 2019-220 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/

class ImageScrollerDescriptor : MenuDescriptor native
{
	native Array<ImageScrollerPage> mItems;
	native Font textFont;
	native TextureID textBackground;
	native Color textBackgroundBrightness;
	native double textScale;
	native bool mAnimatedTransition;
	native bool mAnimated;
	native bool mDontBlur;
	native bool mDontDim;
	native int virtWidth, virtHeight;
}

class ImageScrollerPage : MenuItemBase
{
	int virtWidth, virtHeight;

	protected void DrawText(Font fnt, int color, double x, double y, String text)
	{
		screen.DrawText(fnt, color, x, y, text, DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43);
	}

 	protected void DrawTexture(TextureID tex, double x, double y)
	{
		screen.DrawTexture(tex, true, x, y, DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43);
	}

	virtual void OnStartPage()
	{}

	virtual void OnEndPage()
	{}
}

//=============================================================================
//
// an image page
//
//=============================================================================

class ImageScrollerPageImageItem : ImageScrollerPage
{
	TextureID mTexture;

	void Init(ImageScrollerDescriptor desc, String patch)
	{
		Super.Init();
		mTexture = TexMan.CheckForTexture(patch);
	} 

	override void Drawer(bool selected)
	{
		Screen.DrawTexture(mTexture, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal);
	}
}

//=============================================================================
//
// a simple text page
//
//=============================================================================

class ImageScrollerPageTextItem : ImageScrollerPage
{
	Font mFont;
	BrokenLines mText;
	TextureID mTexture;
	Color mBrightness;
	double mTextScale;

	void Init(ImageScrollerDescriptor desc, String txt, int y = -1)
	{
		Super.Init();
		mTexture = desc.textBackground;
		mBrightness = desc.textBackgroundBrightness;
		mFont = desc.textFont;
		mTextScale = desc.textScale;
		virtWidth = desc.virtWidth;
		virtHeight = desc.virtHeight;

		mText = mFont.BreakLines(Stringtable.Localize(txt.Filter()), int(virtWidth / mTextScale));
		mYpos = y >= 0? y : virtHeight / 2 - mText.Count() * mFont.GetHeight() * mTextScale / 2;

	} 

	override void Drawer(bool selected)
	{
		Screen.DrawTexture(mTexture, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_Color, mBrightness);

		let fontheight = mFont.GetHeight() * mTextScale;
		let y = mYpos;
		let c = mText.Count();
		for (int i = 0; i < c; i++)
		{
			screen.DrawText (mFont, Font.CR_UNTRANSLATED, virtWidth/2 - mText.StringWidth(i) * mTextScale / 2, y, mText.StringAt(i), DTA_ScaleX, mTextScale, DTA_ScaleY, mTextScale,
				DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43);
			y += fontheight;
		}
	}
}

//=============================================================================
//
// The main class
//
//=============================================================================

class ImageScrollerMenu : Menu
{
	ImageScrollerPage previous;
	ImageScrollerPage current;

	double start;
	int length;
	int dir;
	int index;
	ImageScrollerDescriptor mDesc;


	private void StartTransition(ImageScrollerPage to, int animtype)
	{
		if (AnimatedTransition)
		{
			start = MSTimeF() * (120. / 1000.);
			length = 30;
			dir = animtype;
			previous = current;
		}
		to.onStartPage();
		current = to;
	}

	virtual void Init(Menu parent, ImageScrollerDescriptor desc)
	{
		mParentMenu = parent;
		index = 0;
		mDesc = desc;
		AnimatedTransition = mDesc.mAnimatedTransition;
		Animated = mDesc.mAnimated;
		DontBlur = mDesc.mDontBlur;
		DontDim = mDesc.mDontDim;
		current = mDesc.mItems[0];
		current.onStartPage();
		previous = null;
	}

	//=============================================================================
	//
	// 
	//
	//=============================================================================

	override bool MenuEvent(int mkey, bool fromcontroller)
	{
		if (mDesc.mItems.Size() <= 1)
		{
			if (mkey == MKEY_Enter) mkey = MKEY_Back;
			else if (mkey == MKEY_Right || mkey == MKEY_Left) return true;
		}
		switch (mkey)
		{
		case MKEY_Back:
			// Before going back the currently running transition must be terminated.
			previous = null;
			return Super.MenuEvent(mkey, fromcontroller);


		case MKEY_Left:
			if (previous == null)
			{
				if (--index < 0) index = mDesc.mItems.Size() - 1;
				let next = mDesc.mItems[index];
				StartTransition(next, -1);
				MenuSound("menu/choose");
			}
			return true;

		case MKEY_Right:
		case MKEY_Enter:
			if (previous == null)
			{
				int oldindex = index;
				if (++index >= mDesc.mItems.Size()) index = 0;
				let next = mDesc.mItems[index];
				StartTransition(next, 1);
				MenuSound("menu/choose");
			}
			return true;

		default:
			return Super.MenuEvent(mkey, fromcontroller);
		}
	}

	//=============================================================================
	//
	// 
	//
	//=============================================================================

	override bool MouseEvent(int type, int x, int y)
	{
		// Todo: Implement some form of drag event to switch between pages.
		if (type == MOUSE_Release)
		{
			return MenuEvent(MKEY_Enter, false);
		}
		return Super.MouseEvent(type, x, y);
	}

	//=============================================================================
	//
	// 
	//
	//=============================================================================

	private bool DrawTransition()
	{
		double now = MSTimeF() * (120. / 1000.);
		if (now < start + length)
		{
			double factor = screen.GetWidth()/2;
			double phase = (now - start) / length * 180. + 90.;

			screen.SetOffset(factor * dir * (sin(phase) - 1.), 0);
			previous.Drawer(false);
			screen.SetOffset(factor * dir * (sin(phase) + 1.), 0);
			current.Drawer(false);
			screen.SetOffset(0, 0);
			return true;
		}
		previous.OnEndPage();
		previous = null;
		return false;
	}

	//=============================================================================
	//
	// 
	//
	//=============================================================================

	override void Drawer()
	{
		if (previous != null)
		{
			bool wasAnimated = Animated;
			Animated = true;
			if (DrawTransition()) return;
			previous = null;
			Animated = wasAnimated;
		}
		current.Drawer(false);
	}
}
