/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */


#include "common/endian.h"
#include "common/util.h"
#include "common/events.h"
#include "common/system.h"
#include "common/textconsole.h"

#include "sky/disk.h"
#include "sky/intro.h"
#include "sky/music/musicbase.h"
#include "sky/screen.h"
#include "sky/sky.h"
#include "sky/sound.h"
#include "sky/struc.h"
#include "sky/text.h"

#include "audio/audiostream.h"
#include "audio/decoders/raw.h"

namespace Sky {

#define SHOWSCREEN		 0
#define COMMANDEND		 0 // end of COMMANDFLIRT block
#define FADEUP			 1 // fade up palette
#define FADEDOWN		 2
#define DELAY			 3
#define DOFLIRT			 4 // start flirt sequence (and wait for it to finish)
#define SCROLLFLIRT		 5 // start special floppy intro flirt sequence (and wait for it)
#define COMMANDFLIRT	 6 // start flirt sequence and wait for it, while processing command block
#define BGFLIRT			 7 // start flirt sequence without waiting for it
#define WAITFLIRT		 8 // wait for sequence started by BGFLIRT
#define STOPFLIRT		 9
#define STARTMUSIC		10
#define WAITMUSIC		11
#define PLAYVOICE		12
#define WAITVOICE		13
#define LOADBG			14 // load new background sound
#define PLAYBG			15 // play background sound
#define LOOPBG			16 // loop background sound
#define STOPBG			17 // stop background sound
#define SEQEND		 65535 // end of intro sequence

#define IC_PREPARE_TEXT 20 // commands used in COMMANDFLIRT block
#define IC_SHOW_TEXT    21
#define IC_REMOVE_TEXT  22
#define IC_MAKE_SOUND   23
#define IC_FX_VOLUME    24

#define FRAME_SIZE (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT)
#define INTRO_TEXT_WIDTH 128
//CD intro file defines
#define CDV_00	59500
#define CD_PAL	59501
#define CD_1_LOG	59502
#define CD_1	59503
#define CDV_01	59504
#define CDV_02	59505
#define CD_2	59506
#define CDV_03	59507
#define CDV_04	59508
#define CD_3	59509
#define CDV_05	59510
#define CDV_06	59511
#define CD_5	59512
#define CDV_07	59513
#define CDV_08	59514
#define CDV_09	59515
#define CD_7	59516
#define CDV_10	59518
#define CD_11	59519
#define CDV_11	59520
#define CD_11_PAL	59521
#define CD_11_LOG	59522
#define CDV_12	59523
#define CD_13	59524
#define CDV_13	59525
#define CDV_14	59527
#define CDV_15	59528
#define CD_15_PAL	59529
#define CD_15_LOG	59530
#define CDV_16	59531
#define CD_17_LOG	59532
#define CD_17	59533
#define CDV_17	59534
#define CDV_18	59535
#define CDV_19	59536
#define CD_19_PAL	59537
#define CD_19_LOG	59538
#define CDV_20	59539
#define CD_20_LOG	59540
#define CDV_21	59541
#define CD_21_LOG	59542
#define CDV_22	59545
#define CDV_23	59546
#define CD_23_PAL	59547
#define CD_24_LOG	59550
#define CDV_24	59551
#define CDV_25	59554
#define CDV_26	59556
#define CD_27	59557
#define CDV_27	59558
#define CD_27_PAL	59559
#define CD_27_LOG	59560
#define CDV_28	59561
#define CDV_29	59562
#define CDV_30	59563
#define CDV_31	59565
#define CDV_32	59566
#define CDV_33	59567
#define CDV_34	59568
#define CD_35	59569
#define CDV_35	59570
#define CD_35_PAL	59571
#define CD_35_LOG	59572
#define CDV_36	59574
#define CD_37	59575
#define CDV_37	59576
#define CD_37_PAL	59577
#define CD_37_LOG	59578
#define CDV_38	59579
#define CDV_39	59581
#define CDV_40	59583
#define CD_40_PAL	59584
#define CD_40_LOG	59585
#define CDV_41	59587
#define CDV_42	59588
#define CD_43	59589
#define CDV_43	59590
#define CD_43_PAL	59591
#define CD_43_LOG	59592
#define CDV_44	59594
#define CD_45	59595
#define CDV_45	59596
#define CD_45_PAL	59597
#define CD_45_LOG	59598
#define CDV_46	59600
#define CDV_47	59602
#define CD_47_PAL	59603
#define CD_47_LOG	59604
#define CD_48	59605
#define CDV_48	59606
#define CD_48_PAL	59607
#define CD_48_LOG	59608
#define CD_49	59609
#define CDV_49	59610
#define CD_50	59611
#define CDV_50	59612
#define CDV_51	59613
#define CDV_52	59614
#define CDV_53	59615
#define CDV_54	59616
#define CDV_55	59618
#define CD_55_PAL	59619
#define CD_55_LOG	59620
#define CDV_56	59621
#define CDV_57	59622
#define CD_58	59623
#define CDV_58	59624
#define CD_58_PAL	59625
#define CD_58_LOG	59626
#define CDV_59	59627
#define CDV_60	59628
#define CDV_61	59629
#define CDV_62	59630
#define CDV_63	59631
#define CDV_64	59632
#define CDV_65	59633
#define CDV_66	59635
#define CD_66_PAL	59636
#define CD_66_LOG	59637
#define CDV_67	59639
#define CD_67_PAL	59640
#define CD_67_LOG	59641
#define CDV_68	59642
#define CD_69	59643
#define CDV_69	59644
#define CD_69_PAL	59645
#define CD_69_LOG	59646
#define CDV_70	59647
#define CDV_71	59648
#define CDV_72	59649
#define CD_72_PAL	59650
#define CD_72_LOG	59651
#define CD_73_PAL	59652
#define CD_73_LOG	59653
#define CDV_73	59654
#define CDV_74	59655
#define CDV_75	59656
#define CD_76_PAL	59657
#define CD_76_LOG	59658
#define CDV_76	59659
#define CDV_77	59660
#define CD_78_PAL	59661
#define CD_78_LOG	59662
#define CDV_78	59663
#define CDV_79	59664
#define CDV_80	59665
#define CDV_81	59666
#define CDV_82	59667
#define CDV_83	59668
#define CDV_84	59669
#define CDV_85	59670
#define CDV_86	59671
#define CDV_87	59672
#define CD_100	60087
#define CD_101_LOG	60088
#define CD_101	60099
#define CD_102_LOG	60090
#define CD_102	60091
#define CD_103_PAL	60092
#define CD_103_LOG	60093
#define CD_103	60094
#define CD_104_PAL	60095
#define CD_104_LOG	60096
#define CD_104	60097
#define CD_105	60098


uint16 Intro::_mainIntroSeq[] = {
	DELAY,       3000, // keep virgin screen up
	FADEDOWN,
	SHOWSCREEN, 60112, // revo screen + palette
	FADEUP,     60113,
	DELAY,       8000,
	FADEDOWN,
	SHOWSCREEN, 60114, // gibbo screen + palette
	FADEUP,     60115,
	DELAY,       2000,
	FADEDOWN,
	SEQEND
};

uint16 Intro::_cdIntroSeq[] = {
	/* black screen */
	PLAYVOICE,	CDV_00,	// Foster: "The old man was trying to tell the future. Looking for pictures in the campfire..."
	LOADBG,		59499,	// Fire crackle
	LOOPBG,
	WAITVOICE,
	PLAYVOICE,	CDV_01,	// Shaman: "ohhh, I see evil..."
	/* Fade up shaman image while he says his line... */
	SHOWSCREEN,	CD_1_LOG,
	FADEUP,		CD_PAL,
	/* And then play the animation showing the shadows of the fire on his face */
	BGFLIRT,	CD_1,
		WAITVOICE,
		PLAYVOICE,	CDV_02,	// Shaman: "Evil born deep beneath the city... far from the light of day."
		WAITVOICE,
	STOPFLIRT,
	BGFLIRT,	CD_2,
		PLAYVOICE,	CDV_03, // Shaman: "I see it growing, safe beneath a sky of steel..."
		WAITVOICE,
		PLAYVOICE,	CDV_04, // Shaman: "Scheming in the dark... gathering strength."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_05,		// Shaman: "And now... ohhh.... now the evil spreads..."
	DELAY,		2000,
	BGFLIRT,	CD_3,
		WAITVOICE,
		PLAYVOICE,	CDV_06,	// Shaman: "It sends deadly feelers over the land above..."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_07,		// Shaman: "Across the gap... reaching towards this very place!"
	BGFLIRT,	CD_5,
		WAITVOICE,
		PLAYVOICE,	CDV_08,	// Foster: "I'd seen him do this a hundred times, but I humoured him."
		WAITVOICE,
		PLAYVOICE,	CDV_09,	// Foster: "After all, he'd been like a father to me."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_10,		// Foster: "And what does this evil want here?"
	BGFLIRT,	CD_7,
		WAITVOICE,
		PLAYVOICE,	CDV_11, // Shaman: "Oh, my son. I fear..."
	WAITFLIRT,
	FADEDOWN,
	SHOWSCREEN,	CD_11_LOG,
	FADEUP,		CD_11_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_12,		// Shaman: "I fear the evil wants you!"
	DELAY,		1600,
	BGFLIRT,	CD_11,
		WAITVOICE,
		PLAYVOICE,	CDV_13,	// Foster: "That was when Joey piped up..."
		WAITVOICE,
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_14,		// Joey: "Foster! Sensors detect incoming audio source!"
	LOADBG,		59498, // fire crackle to heli start
	PLAYBG,
	DOFLIRT,	CD_13,
	WAITVOICE,
	PLAYVOICE,	CDV_15,		// Shaman: "The evil! The evil is nearly here...!"
	FADEDOWN,
	SHOWSCREEN,	CD_15_LOG,
	FADEUP,		CD_15_PAL,
	WAITVOICE,
	LOADBG,		59496, // quiet heli
	LOOPBG,
	PLAYVOICE,	CDV_16,		// Foster: "It sounded more like a 'copter than a demon."
	WAITVOICE,
	PLAYVOICE,	CDV_17,		// Foster: "But next thing, all hell let loose anyway..."
	DELAY,		2000,
	SHOWSCREEN,	CD_17_LOG,
	WAITVOICE,
	BGFLIRT,	CD_17,
		PLAYVOICE,	CDV_18,	// Shaman: "Run, Foster! Run! Hide from the evil!"
	LOADBG,		59497, // loud heli
	LOOPBG,
	WAITFLIRT,
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_19_LOG,
	FADEUP,		CD_19_PAL,
	PLAYVOICE,	CDV_19,		// Joey: "Foster! (zzzt) H-Help!"
	WAITVOICE,
	PLAYVOICE,	CDV_20,		// Joey: "Better make my next body move faster, Foster..."
	FADEDOWN,
	SHOWSCREEN,	CD_20_LOG,
	FADEUP,		CD_19_PAL,
	WAITVOICE,
	LOADBG,		59496, // quiet heli
	LOOPBG,
	PLAYVOICE,	CDV_21,		// Foster: "He was only a robot, but, well, I loved the little guy."
	FADEDOWN,
	SHOWSCREEN,	CD_21_LOG,
	FADEUP,		CD_19_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_22,		// Foster: "Then, as suddenly as it started, the shooting stopped."
	LOADBG,		59494, // heli whine
	PLAYBG,
	WAITVOICE,
	PLAYVOICE,	CDV_23,		// Foster: "There was a moment's silence as the copter cut its rotors, then..."
	/* fade down while Foster's saying his line */
	FADEDOWN,
	WAITVOICE,
	SHOWSCREEN,	CD_24_LOG,
	FADEUP,		CD_23_PAL,
	PLAYVOICE,	CDV_24,		// Reich: "Whoever is in charge here, come forward..."
	WAITVOICE,
	PLAYVOICE,	CDV_25,		// Reich: "Now!!"
	WAITVOICE,
	PLAYVOICE,	CDV_26,		// Foster: "Only a fool would have argued with that firepower."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_27_LOG,
	FADEUP,		CD_27_PAL,
	PLAYVOICE,	CDV_27,		// Shaman: "... I am the leader of these people... We are peaceful..."
	WAITVOICE,
	PLAYVOICE,	CDV_29,		// Reich: "Bring him here."
	WAITVOICE,
	PLAYVOICE,	CDV_30,		// Guard: "At once, Commander Reich."
	WAITVOICE,
	BGFLIRT,	CD_27,
		PLAYVOICE,	CDV_31,	// Reich: "We're looking for someone..."
		WAITVOICE,
		PLAYVOICE,	CDV_32,	// Reich: "Someone who doesn't belong here..."
		WAITVOICE,
		PLAYVOICE,	CDV_33,	// Reich: "Who wasn't born in this garbage dump..."
		WAITVOICE,
		PLAYVOICE,	CDV_34,	// Reich: "Who came from the city as a child..."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_35,		// Reich: "We want to take him home again."
	WAITVOICE,
	PLAYVOICE,	CDV_36,		// Foster: "My mind racing, I remembered where I'd seen that symbol before..."
		FADEDOWN,
		SHOWSCREEN,	CD_35_LOG,
		FADEUP,		CD_35_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_37,		// Foster: "It was the day the tribe found me..."
		DOFLIRT,	CD_35,
	WAITVOICE,
	PLAYVOICE,	CDV_38,		// Foster: "The day of the crash..."
		DOFLIRT,	CD_37,
	WAITVOICE,
	PLAYVOICE,	CDV_39,		// Foster: "The day my mother died."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_40_LOG,
	FADEUP,		CD_40_PAL,
	PLAYVOICE,	CDV_40,		// Shaman: "You alright, city boy?"
	WAITVOICE,
	PLAYVOICE,	CDV_41,		// Shaman: "Got a name, son?"
	WAITVOICE,
	PLAYVOICE,	CDV_42,		// Foster: "R-Robert."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_43_LOG,
	FADEUP,		CD_43_PAL,
	PLAYVOICE,	CDV_43,		// Shaman: "Hah! Welcome to the Gap, Robert!"
	WAITVOICE,
	DOFLIRT,	CD_43,
	PLAYVOICE,	CDV_45,		// Foster: "As he patched me up, the old man had gently explained that there was no way back into the City..."
	FADEDOWN,
	SHOWSCREEN,	CD_45_LOG,
	FADEUP,		CD_45_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_46,		// Foster: "And I already knew there was nothing he could do for mother."
	DOFLIRT,	CD_45,
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_47_LOG,
	FADEUP,		CD_47_PAL,
	PLAYVOICE,	CDV_47,		// Foster: "His tribe was poor, but they treated me like one of their own..."
	WAITVOICE,
	PLAYVOICE,	CDV_48,		// Foster: "I learned how to survive in the wasteland they called the Gap..."
	FADEDOWN,
	SHOWSCREEN,	CD_48_LOG,
	FADEUP,		CD_48_PAL,
	WAITVOICE,
	BGFLIRT,	CD_48,
		PLAYVOICE,	CDV_49,	// Foster: "And scavenging from the City dumps."
		WAITVOICE,
		PLAYVOICE,	CDV_50,	// Foster: "As the years passed, I forgot my life in the City."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_51,		// Foster: "Discovered new talents..."
	BGFLIRT,	CD_49,
		WAITVOICE,
		PLAYVOICE,	CDV_52,	// Foster: "Hah!"
		WAITVOICE,
		PLAYVOICE,	CDV_53,	// Joey: "I'm your (zzzt) friend... call me (zzzt) Joey."
		WAITVOICE,
	WAITFLIRT,
	PLAYVOICE,	CDV_54,		// Foster: "And got a second name."
	DOFLIRT,	CD_50,
	WAITVOICE,
	PLAYVOICE,	CDV_55,		// Shaman: "This is what we'll call you now you've come of age, son."
	WAITVOICE,
	PLAYVOICE,	CDV_56,		// Shaman: "We found you... we fostered you..."
		FADEDOWN,
		SHOWSCREEN,	CD_55_LOG,
		FADEUP,		CD_55_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_57,		// Shaman: "So that makes you Robert Foster."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_58_LOG,
	FADEUP,		CD_58_PAL,
	PLAYVOICE,	CDV_58,		// Reich: "...Wasted enough time!"
	WAITVOICE,
	PLAYVOICE,	CDV_59,		// Reich: "Give us the runaway or we'll shoot everyone..."
	WAITVOICE,
	PLAYVOICE,	CDV_60,		// Reich: "Starting with you, grandad!"
	WAITVOICE,
	PLAYVOICE,	CDV_61,		// Foster: "The old man had been right, for once..."
	WAITVOICE,
	PLAYVOICE,	CDV_62,		// Foster: "It was me they wanted."
	BGFLIRT,	CD_58,
		WAITVOICE,
		PLAYVOICE,	CDV_63,	// Shaman: "No, my son! Don't let the evil take you! Run!"
		WAITVOICE,
		PLAYVOICE,	CDV_64,	// Guard: "DNA scan confirms it's him, sir."
	WAITFLIRT,
	WAITVOICE,
	PLAYVOICE,	CDV_65,		// Foster: "Evil had come to the Gap, just as he said."
	FADEDOWN,
	WAITVOICE,
	SHOWSCREEN,	CD_66_LOG,
	FADEUP,		CD_66_PAL,
	PLAYVOICE,	CDV_66,		// Reich: "Take him."
	WAITVOICE,
	PLAYVOICE,	CDV_67,		// Foster: "But had the old man seen why it wanted me?"
		FADEDOWN,
		SHOWSCREEN,	CD_67_LOG,
		FADEUP,		CD_67_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_68,		// Foster: "Or what it would do next?"
	WAITVOICE,
	PLAYVOICE,	CDV_69,		// Foster: "It was too late to ask him now."
		FADEDOWN,
		SHOWSCREEN,	CD_69_LOG,
		FADEUP,		CD_69_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_70,		// Guard: "Leaving destruction zone, Commander Reich."
	DOFLIRT,	CD_69,
	WAITVOICE,
	FADEDOWN,
	PLAYVOICE,	CDV_71,		// Reich: "Good. Detonate."
	WAITVOICE,
	SHOWSCREEN,	CD_72_LOG,
	FADEUP,		CD_72_PAL,
	PLAYVOICE,	CDV_72,		// Foster: "Much too late."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_73_LOG,
	FADEUP,		CD_73_PAL,
	PLAYVOICE,	CDV_73,		// Foster: "Why, you murdering..."
	WAITVOICE,
	PLAYVOICE,	CDV_74,		// Reich: "Keep him quiet."
	WAITVOICE,
	PLAYVOICE,	CDV_75,		// Foster: "All I could do was wait."
	FADEDOWN,
	SHOWSCREEN,	CD_76_LOG,
	FADEUP,		CD_76_PAL,
	WAITVOICE,
	PLAYVOICE,	CDV_76,		// Foster: "Just like on a hunt. Just like the old man taught me."
	WAITVOICE,
	PLAYVOICE,	CDV_77,		// Foster: "Wait... and be ready."
	WAITVOICE,
	FADEDOWN,
	SHOWSCREEN,	CD_78_LOG,
	FADEUP,		CD_78_PAL,
	PLAYVOICE,	CDV_78,		// Foster: "It was dawn when we reached the City."
	WAITVOICE,
	PLAYVOICE,	CDV_79,		// Reich: "Land in the central Security compound."
	WAITVOICE,
	PLAYVOICE,	CDV_80,		// Foster: "A dawn my tribe would never see."
	BGFLIRT,	CD_100,
		WAITVOICE,
		PLAYVOICE,	CDV_81,	// Foster: "They were no more than a note in Reich's book now."
		WAITVOICE,
		PLAYVOICE,	CDV_82,	// Guard: "Yes, sir. Locking on automatic landing beacon."
		WAITVOICE,
	WAITFLIRT,
	SHOWSCREEN,	CD_101_LOG,
	BGFLIRT,	CD_101,
		PLAYVOICE,	CDV_83,	// Foster: "But what was I? Why did..."
		WAITVOICE,
		PLAYVOICE,	CDV_84,	// Guard: "Sir! The guidance system! It's gone crazy!"
		WAITVOICE,
		PLAYVOICE,	CDV_85,	// Guard: "We're going to HIT!"
		WAITVOICE,
	WAITFLIRT,
	SHOWSCREEN,	CD_102_LOG,
	PLAYVOICE,	CDV_86,		// Foster: "Maybe I'd get some answers now."
	DOFLIRT,	CD_102,
	FADEDOWN,
	SHOWSCREEN,	CD_103_LOG,
	FADEUP,		CD_103_PAL,
	BGFLIRT,	CD_103,
	WAITVOICE,
	PLAYVOICE,	CDV_87,		// Foster: "If I survived another 'copter crash."
	WAITFLIRT,
	WAITVOICE,
	STARTMUSIC,	2,
	FADEDOWN,
	SHOWSCREEN,	CD_104_LOG,
	FADEUP,		CD_104_PAL,
	DOFLIRT,	CD_104,
	DOFLIRT,	CD_105,
	SEQEND
};

uint16 Intro::_floppyIntroSeq[] = {
	SHOWSCREEN,   60081,
	FADEUP,       60080,
	DOFLIRT,      60082,
	DOFLIRT,      60083,
	DOFLIRT,      60084, // Beneath a Steel Sky
	DOFLIRT,      60085,
	DOFLIRT,      60086,
	SCROLLFLIRT,
	COMMANDFLIRT, 60087, // => command list 4a
		136, IC_MAKE_SOUND,  1, 70,
		 90, IC_FX_VOLUME,  80,
		 50, IC_FX_VOLUME,  90,
		  5, IC_FX_VOLUME, 100,
	COMMANDEND,
	SHOWSCREEN,   60088,
	COMMANDFLIRT, 60089, // => command list 4b (cockpit)
		1000, IC_PREPARE_TEXT,  77,
		 220, IC_SHOW_TEXT,     20, 160, // radar detects jamming signal
		 105, IC_REMOVE_TEXT,
		 105, IC_PREPARE_TEXT,  81,
		 105, IC_SHOW_TEXT,    170,  86, // well switch to override you fool
		  35, IC_REMOVE_TEXT,
		  35, IC_PREPARE_TEXT, 477,
		  35, IC_SHOW_TEXT,     30, 160,
		   3, IC_REMOVE_TEXT,
	COMMANDEND,
	SHOWSCREEN,   60090,
	COMMANDFLIRT, 60091, // => command list 4c
		1000, IC_FX_VOLUME, 100,
		  25, IC_FX_VOLUME, 110,
		  15, IC_FX_VOLUME, 120,
		   4, IC_FX_VOLUME, 127,
	COMMANDEND,
	FADEDOWN,
	SHOWSCREEN,  60093,
	FADEUP,       60092,
	COMMANDFLIRT, 60094, // => command list 5
		31, IC_MAKE_SOUND, 2, 127,
	COMMANDEND,
	WAITMUSIC,
	FADEDOWN,
	SHOWSCREEN,   60096,
	STARTMUSIC,       2,
	FADEUP,       60095,
	COMMANDFLIRT, 60097, // => command list 6a
		1000, IC_PREPARE_TEXT, 478,
		  13, IC_SHOW_TEXT,    175, 155,
	COMMANDEND,
	COMMANDFLIRT, 60098, // => command list 6b
		131, IC_REMOVE_TEXT,
		131, IC_PREPARE_TEXT, 479,
		 74, IC_SHOW_TEXT,    175, 155,
		 45, IC_REMOVE_TEXT,
		 45, IC_PREPARE_TEXT, 162,
		 44, IC_SHOW_TEXT,    175, 155,
		  4, IC_REMOVE_TEXT,
	COMMANDEND,
	SEQEND
};

Intro::Intro(Disk *disk, Screen *screen, MusicBase *music, Sound *sound, Text *text, Audio::Mixer *mixer, OSystem *system) {
	_skyDisk = disk;
	_skyScreen = screen;
	_skyMusic = music;
	_skySound = sound;
	_skyText = text;
	_mixer = mixer;
	_system = system;
	_textBuf = (uint8 *)malloc(10000);
	_saveBuf = (uint8 *)malloc(10000);
	_bgBuf = NULL;
	_relDelay = 0;
}

Intro::~Intro() {
	if (_skyScreen->sequenceRunning())
		_skyScreen->stopSequence();

	free(_textBuf);
	free(_saveBuf);
	_mixer->stopID(SOUND_BG);
	free(_bgBuf);
}

bool Intro::doIntro(bool floppyIntro) {
	if (!SkyEngine::isCDVersion())
		floppyIntro = true;

	_skyMusic->loadSection(0);
	_skySound->loadSection(0);

	if (!escDelay(3000))
		return false;

	if (floppyIntro)
		_skyMusic->startMusic(1);

	uint16 *seqData = _mainIntroSeq;
	while (*seqData != SEQEND) {
		if (!nextPart(seqData))
			return false;
	}
	if (floppyIntro)
		seqData = _floppyIntroSeq;
	else
		seqData = _cdIntroSeq;

	while (*seqData != SEQEND) {
		if (!nextPart(seqData))
			return false;
	}
	return true;
}

bool Intro::nextPart(uint16 *&data) {
	uint8 *vData = NULL;
	Audio::RewindableAudioStream *stream = 0;

	// return false means cancel intro
	uint16 command = *data++;
	switch (command) {
	case SHOWSCREEN:
		_skyScreen->showScreen(*data++);
		return true;
	case FADEUP:
		_skyScreen->paletteFadeUp(*data++);
		_relDelay += 32 * 20; // hack: the screen uses a seperate delay function for the
							  // blocking fadeups. So add 32*20 msecs to out delay counter.
		return true;
	case FADEDOWN:
		_skyScreen->fnFadeDown(0);
		_relDelay += 32 * 20; // hack: see above.
		return true;
	case DELAY:
		if (!escDelay(*data++))
			return false;
		return true;
	case DOFLIRT:
		_skyScreen->startSequence(*data++);
		while (_skyScreen->sequenceRunning())
			if (!escDelay(50))
				return false;
		return true;
	case SCROLLFLIRT:
		return floppyScrollFlirt();
	case COMMANDFLIRT:
		return commandFlirt(data);
	case STOPFLIRT:
		_skyScreen->stopSequence();
		return true;
	case STARTMUSIC:
		_skyMusic->startMusic(*data++);
		return true;
	case WAITMUSIC:
		while (_skyMusic->musicIsPlaying())
			if (!escDelay(50))
				return false;
		return true;
	case BGFLIRT:
		_skyScreen->startSequence(*data++);
		return true;
	case WAITFLIRT:
		while (_skyScreen->sequenceRunning())
			if (!escDelay(50))
				return false;
		return true;
	case PLAYVOICE:
		if (!escDelay(200))
			return false;
		vData = _skyDisk->loadFile(*data++);
		// HACK: Fill the header with silence. We should
		// probably use _skySound instead of calling playStream()
		// directly, but this will have to do for now.
		memset(vData, 127, sizeof(DataFileHeader));

		stream = Audio::makeRawStream(vData, _skyDisk->_lastLoadedFileSize, 11025, Audio::FLAG_UNSIGNED);
		_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_voice, stream, SOUND_VOICE);
		return true;
	case WAITVOICE:
		while (_mixer->isSoundHandleActive(_voice))
			if (!escDelay(50))
				return false;
		return true;
	case LOADBG:
		_mixer->stopID(SOUND_BG);
		free(_bgBuf);
		_bgBuf = _skyDisk->loadFile(*data++);
		_bgSize = _skyDisk->_lastLoadedFileSize;
		return true;
	case LOOPBG:
		_mixer->stopID(SOUND_BG);
		stream = Audio::makeRawStream(_bgBuf + 256, _bgSize - 768, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSfx, Audio::makeLoopingAudioStream(stream, 0), SOUND_BG);
		return true;
	case PLAYBG:
		_mixer->stopID(SOUND_BG);
		stream = Audio::makeRawStream(_bgBuf + 256, _bgSize - 768, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSfx, stream, SOUND_BG);
		return true;
	case STOPBG:
		_mixer->stopID(SOUND_BG);
		return true;
	default:
		error("Unknown intro command %X", command);
	}
	return true;
}

bool Intro::floppyScrollFlirt() {
	uint8 *scrollScreen = (uint8 *)malloc(FRAME_SIZE * 2);
	memset(scrollScreen, 0, FRAME_SIZE);
	memcpy(scrollScreen + FRAME_SIZE, _skyScreen->giveCurrent(), FRAME_SIZE);
	uint8 *scrollPos = scrollScreen + FRAME_SIZE;
	uint8 *vgaData = _skyDisk->loadFile(60100);
	uint8 *diffData = _skyDisk->loadFile(60101);
	uint16 frameNum = READ_LE_UINT16(diffData);
	uint8 *diffPtr = diffData + 2;
	uint8 *vgaPtr = vgaData;
	bool doContinue = true;

	for (uint16 frameCnt = 1; (frameCnt < frameNum) && doContinue; frameCnt++) {
		uint8 scrollVal = *diffPtr++;
		if (scrollVal)
			scrollPos -= scrollVal * GAME_SCREEN_WIDTH;

		uint16 scrPos = 0;
		while (scrPos < FRAME_SIZE) {
			uint8 nrToDo, nrToSkip;
			do {
				nrToSkip = *diffPtr++;
				scrPos += nrToSkip;
			} while (nrToSkip == 255);
			do {
				nrToDo = *diffPtr++;
				memcpy(scrollPos + scrPos, vgaPtr, nrToDo);
				scrPos += nrToDo;
				vgaPtr += nrToDo;
			} while (nrToDo == 255);
		}
		_system->copyRectToScreen(scrollPos, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
		_system->updateScreen();
		if (!escDelay(60))
			doContinue = false;
	}
	memcpy(_skyScreen->giveCurrent(), scrollPos, FRAME_SIZE);
	free(diffData);
	free(vgaData);
	free(scrollScreen);
	return doContinue;
}

bool Intro::commandFlirt(uint16 *&data) {
	_skyScreen->startSequence(*data++);

	while ((*data != COMMANDEND) || _skyScreen->sequenceRunning()) {
		while ((_skyScreen->seqFramesLeft() < *data)) {
			data++;
			uint16 command = *data++;
			switch (command) {
			case IC_PREPARE_TEXT:
				_skyText->displayText(*data++, _textBuf, true, INTRO_TEXT_WIDTH, 255);
				break;
			case IC_SHOW_TEXT:
				((DataFileHeader *)_textBuf)->s_x = *data++;
				((DataFileHeader *)_textBuf)->s_y = *data++;
				showTextBuf();
				break;
			case IC_REMOVE_TEXT:
				restoreScreen();
				break;
			case IC_MAKE_SOUND:
				_skySound->playSound(data[0], data[1], 0);
				data += 2;
				break;
			case IC_FX_VOLUME:
				_skySound->playSound(1, *data++, 0);
				break;
			default:
				error("Unknown FLIRT command %X", command);
			}
		}

		if (!escDelay(50)) {
			_skyScreen->stopSequence();
			return false;
		}
	}

	data++; // move pointer over "COMMANDEND"
	return true;
}

void Intro::showTextBuf() {
	uint16 x = ((DataFileHeader *)_textBuf)->s_x;
	uint16 y = ((DataFileHeader *)_textBuf)->s_y;
	uint16 width = ((DataFileHeader *)_textBuf)->s_width;
	uint16 height = ((DataFileHeader *)_textBuf)->s_height;
	uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
	memcpy(_saveBuf, _textBuf, sizeof(DataFileHeader));
	uint8 *saveBuf = _saveBuf + sizeof(DataFileHeader);
	uint8 *textBuf = _textBuf + sizeof(DataFileHeader);
	for (uint16 cnty = 0; cnty < height; cnty++) {
		memcpy(saveBuf, screenBuf, width);
		for (uint16 cntx = 0; cntx < width; cntx++)
			if (textBuf[cntx])
				screenBuf[cntx] = textBuf[cntx];
		screenBuf += GAME_SCREEN_WIDTH;
		textBuf += width;
		saveBuf += width;
	}
	screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
	_system->copyRectToScreen(screenBuf, GAME_SCREEN_WIDTH, x, y, width, height);
}

void Intro::restoreScreen() {
	uint16 x = ((DataFileHeader *)_saveBuf)->s_x;
	uint16 y = ((DataFileHeader *)_saveBuf)->s_y;
	uint16 width = ((DataFileHeader *)_saveBuf)->s_width;
	uint16 height = ((DataFileHeader *)_saveBuf)->s_height;
	uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
	uint8 *saveBuf = _saveBuf + sizeof(DataFileHeader);
	for (uint16 cnt = 0; cnt < height; cnt++) {
		memcpy(screenBuf, saveBuf, width);
		screenBuf += GAME_SCREEN_WIDTH;
		saveBuf += width;
	}
	_system->copyRectToScreen(_saveBuf + sizeof(DataFileHeader), width, x, y, width, height);
}

bool Intro::escDelay(uint32 msecs) {
	Common::EventManager *eventMan = _system->getEventManager();
	Common::Event event;

	if (_relDelay == 0) // first call, init with system time
		_relDelay = (int32)_system->getMillis();

	_relDelay += msecs; // now wait until _system->getMillis() >= _relDelay

	int32 nDelay = 0;
	do {
		while (eventMan->pollEvent(event)) {
			if (event.type == Common::EVENT_KEYDOWN) {
				if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
					return false;
			} else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL) {
				return false;
			}
		}
		nDelay = _relDelay - _system->getMillis();
		if (nDelay < 0)
			nDelay = 0;
		else if (nDelay > 20)
			nDelay = 20;

		_system->delayMillis(nDelay);

		_skyScreen->processSequence();
		_system->updateScreen();
	} while (nDelay == 20);

	return true;
}

} // End of namespace Sky
