// Need to be filled in:
function PlayBladeDamage() {}
function PlayGrabCoin() {}

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

const shadowOffset = 5;

function Blade(point, texture)
{
    this.point = point;

	this.element1 = document.createElement('img');
	this.element1Shadow = document.createElement('img');
    this.element2 = document.createElement('img');
    this.element2Shadow = document.createElement('img');
    this.axle = document.createElement('img');
    this.axleShadow = document.createElement('img');

	this.element1.src = this.element2.src = 'images/' + texture + '.png';
    this.element1Shadow.src = this.element2Shadow.src = 'images/' + texture + 'Shadow.png';
    this.axle.src = 'images/BronzeBladeAxle.png';
    this.axleShadow.src = 'images/BronzeBladeAxleShadow.png';

    this.element1Shadow.style.opacity = this.element2Shadow.style.opacity = this.axleShadow.style.opacity = .5;

    this.element1.className = 'spinner1';
    this.element1Shadow.className = 'spinner1';
    this.element2.className = 'spinner2';
    this.element2Shadow.className = 'spinner2';

    this.element1.style.left = this.element1Shadow.style.left = this.element2.style.left = this.element2Shadow.style.left = point.x;
    this.element1.style.top = this.element2.style.top = this.point.y - this.element1.height / 2;
    this.element1Shadow.style.top = this.element2Shadow.style.top = this.point.y - this.element1.height / 2 + shadowOffset;

    this.animationListener = bind(this.AnimationCycle, this);
}

Blade.prototype.AnimationCycle = function() { newTime = new Date().getTime(); this.lastAnimCycle = newTime; }

// I had to do it this way to be performant. Animating frame-by-frame from javascript was slow on iPhone and I can't find a way to get the current animated transform.
Blade.prototype.GetAngle = function() { return 2 * Math.PI * (new Date().getTime() - this.lastAnimCycle) / 2000; }

Blade.prototype.AddToContainer = function(container)
{
	container.appendChild(this.element1Shadow);
	container.appendChild(this.element2Shadow);
	container.appendChild(this.axleShadow);
	container.appendChild(this.element1);
	container.appendChild(this.element2);
	container.appendChild(this.axle);

	this.axle.style.left = this.point.x - this.axle.width / 2;
    this.axle.style.top = this.point.y - this.axle.height / 2;
    this.axleShadow.style.left = this.point.x - this.axle.width / 2;
    this.axleShadow.style.top = this.point.y - this.axle.height / 2 + shadowOffset;

    this.element1.addEventListener('webkitAnimationStart', this.animationListener, false);
    this.element1.addEventListener('webkitAnimationIteration', this.animationListener, false);
}

Blade.prototype.RemoveFromContainer = function()
{
    if (this.element1.parentElement)
        this.element1.parentElement.removeChild(this.element1);
    if (this.element1Shadow.parentElement)
        this.element1Shadow.parentElement.removeChild(this.element1Shadow);
    if (this.element2.parentElement)
        this.element2.parentElement.removeChild(this.element2);
    if (this.element2Shadow.parentElement)
        this.element2Shadow.parentElement.removeChild(this.element2Shadow);
    if (this.axle.parentElement)
        this.axle.parentElement.removeChild(this.axle);
    if (this.axleShadow.parentElement)
        this.axleShadow.parentElement.removeChild(this.axleShadow);

    this.element1.removeEventListener('webkitAnimationStart', this.animationListener, false);
    this.element1.removeEventListener('webkitAnimationIteration', this.animationListener, false);
}

Blade.prototype.ContainsPoint = function(point)
{
    dx = point.x - this.point.x;
    dy = point.y - this.point.y;
    distance = Math.sqrt(dx * dx + dy * dy);
    slope = Math.tan(this.GetAngle());
    lineDistance = Math.abs(-slope * dx + dy / Math.sqrt(slope * slope + 1));
    return lineDistance < 15 && distance < this.element1.width;
}

Blade.prototype.angle = 0;
Blade.prototype.element1 = null;
Blade.prototype.element1Shadow = null;
Blade.prototype.element2 = null;
Blade.prototype.element2Shadow = null;
Blade.prototype.axle = null;
Blade.prototype.axleShadow = null;
Blade.prototype.point = { x:0, y:0 };
Blade.prototype.lastAnimCycle = 0;
Blade.prototype.animationListener = null;

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

function Gem(point, kind)
{
    this.point = point;
	this.element = document.createElement('img');
	this.element.src = 'images/' + kind + '.png';
    this.element.style.top = this.point.y - this.element.height / 2;
    this.element.style.left = this.point.x - this.element.width / 2;

    this.cross1 = document.createElement('img');
    this.cross2 = document.createElement('img');
    this.cross1.src = this.cross2.src = 'images/Cross.png';
    this.cross1.className = 'cross1';
    this.cross2.className = 'cross2';
    this.cross1.style.left = point.x - this.cross1.width / 2;
    this.cross1.style.top = point.y - this.cross1.height / 2;

    this.cross2.style.left = point.x - this.cross2.width / 2;
    this.cross2.style.top = point.y - this.cross2.height / 2;
}

Gem.prototype.AddToContainer = function(container)
{
    container.appendChild(this.cross1);
    container.appendChild(this.cross2);
    container.appendChild(this.element);
}

Gem.prototype.RemoveFromContainer = function()
{
    if (this.element.parentElement)
        this.element.parentElement.removeChild(this.element)
    if (this.cross1.parentElement)
        this.cross1.parentElement.removeChild(this.cross1);
    if (this.cross2.parentElement)
        this.cross2.parentElement.removeChild(this.cross2);
}

Gem.prototype.ContainsPoint = function(point)
{
    dx = point.x - this.point.x;
    dy = point.y - this.point.y;
    return Math.sqrt(dx * dx + dy * dy) < 35;
}

Gem.prototype.Grabbed = function()
{
    PlayGrabCoin();
    this.grabbed = true;
    if (this.element.parentElement) this.element.parentElement.removeChild(this.element);
    if (this.cross1.parentElement) this.cross1.parentElement.removeChild(this.cross1);
    this.cross2.className = 'grabbedGemGlow';
}

Gem.prototype.point = { x:0, y:0 };
Gem.prototype.grabbed = false;
Gem.prototype.element = null;
Gem.prototype.cross1 = null;
Gem.prototype.cross2 = null;

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

function Wave(gems)
{
    this.gems = gems;
}

Wave.prototype.AddToContainer = function(container)
{
    for (gem in this.gems)
        this.gems[gem].AddToContainer(container);
}

Wave.prototype.RemoveFromContainer = function()
{
    for (gem in this.gems)
        this.gems[gem].RemoveFromContainer();
}

Wave.prototype.GrabGemsAtPoint = function(point)
{
    for (gem in this.gems)
        if (this.gems[gem].ContainsPoint(point)) {
            this.gems[gem].Grabbed();
            break;
        }
}

Wave.prototype.HasGemsLeft = function()
{
    for (gem in this.gems)
        if (!this.gems[gem].grabbed)
            return true;
    return false;
}

Wave.prototype.gems = [];

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

function Level(blades, waves, backdropName)
{
    this.blades = blades;
    this.waves = waves;
    this.backdropName = backdropName;
}

Level.prototype.AddToContainer = function()
{
    if (this.currentWave < this.waves.length)
        this.waves[this.currentWave].AddToContainer(gems);
    for (blade in this.blades)
        this.blades[blade].AddToContainer(hazards);
    game.style.backgroundImage = 'url(images/' + this.backdropName + '.jpg)';
    this.HealthChanged();
}

Level.prototype.HealthChanged = function()
{
    healthOrbBlood.src = 'images/HealthOrbBlood' + this.health + '.png';
}

Level.prototype.RemoveFromContainer = function()
{
    for (blade in this.blades)
        this.blades[blade].RemoveFromContainer();
    if (this.currentWave < this.waves.length)
        this.waves[this.currentWave].RemoveFromContainer();
}

Level.prototype.BladeHit = function()
{
    PlayBladeDamage();
    this.health -= 1;
    this.HealthChanged();
    blood.className = '';
    window.setTimeout(function() { blood.className = 'bloodsplat'; }, 0);
    this.activeTouchHit = true;
    if (this.health == 0)
        LevelLost();
}

Level.prototype.TouchBegan = function(touch) {
    if (!this.activeTouch) { this.activeTouch = touch; this.activeTouchHit = false; }
    this.CheckTouch();
}

Level.prototype.TouchEnded = function(touch) {
    if (this.activeTouch == touch) {
        if (!this.activeTouchHit) this.GrabGemsAtPoint({ x:touch.pageX, y:touch.pageY });
        this.activeTouch = null;
        this.activeTouchHit = false;
    }
}

Level.prototype.CheckTouch = function()
{
    if (this.activeTouch && !this.activeTouchHit)
        for (blade in this.blades)
            if (this.blades[blade].ContainsPoint({ x:this.activeTouch.pageX, y:this.activeTouch.pageY }))
                this.BladeHit();
}

Level.prototype.GrabGemsAtPoint = function(point)
{
    if (this.currentWave < this.waves.length) {
        this.waves[this.currentWave].GrabGemsAtPoint(point);
        if (!this.waves[this.currentWave].HasGemsLeft()) {
            window.setTimeout(bind(Wave.prototype.RemoveFromContainer, this.waves[this.currentWave]), 500);
            ++this.currentWave;
            if (this.currentWave < this.waves.length)
                window.setTimeout(bind(function() { this.waves[this.currentWave].AddToContainer(gems); }, this), 0);
            else
                LevelWon();
        }
    }
}

Level.prototype.blades = [];
Level.prototype.waves = [];
Level.prototype.activeTouches = [];
Level.prototype.currentWave = 0;
Level.prototype.score = 0;
Level.prototype.health = 3;
Level.prototype.backdropName = null;
Level.prototype.activeTouch = null;
Level.prototype.activeTouchHit = false;

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

function LoadGem(gem) { return new Gem(gem.point, gem.kind); }

function LoadGems(gems)
{
    _gems = [];
    for (gem in gems)
        _gems.push(LoadGem(gems[gem]));
    return _gems;
}

function LoadWave(wave) { return new Wave(LoadGems(wave.gems)); }

function LoadWaves(waves)
{
    _waves = [];
    for (wave in waves)
        _waves.push(LoadWave(waves[wave]));
    return _waves;
}

function LoadBlade(blade) { return new Blade(blade.point, blade.texture); }

function LoadBlades(blades)
{
    _blades = [];
    for (blade in blades)
        _blades.push(LoadBlade(blades[blade]));
    return _blades;
}

function LoadLevel(level) { return new Level(LoadBlades(level.blades), LoadWaves(level.waves), level.backdropName); }

levels = [
    /* Level 1 */ {
        blades: [
            { point: { x:320 / 2, y:480 / 2 }, texture: 'Scimitar' }
        ],
        waves: [
            /* Wave 1 */ {
                gems: [
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'GoldCoin' },
                    { point: { x:120, y:250 }, kind: 'GoldCoin' }
                ]
            },
            /* Wave 2 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'GoldCoin' },
                    { point: { x:75, y:150 }, kind: 'GoldCoin' },
                    { point: { x:270, y:350 }, kind: 'GoldCoin' },
                    { point: { x:100, y:300 }, kind: 'GoldCoin' }
                ],
            },
            /* Wave 3 */ {
                gems: [
                    { point: { x:75, y:250 }, kind: 'GoldCoin' },
                    { point: { x:200, y:150 }, kind: 'GoldCoin' },
                    { point: { x:100, y:350 }, kind: 'GoldCoin' },
                    { point: { x:300, y:300 }, kind: 'GoldCoin' },
                    { point: { x:300, y:100 }, kind: 'GoldCoin' }
                ]
            },
            /* Wave 4 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'GoldCoin' },
                    { point: { x:75, y:150 }, kind: 'GoldCoin' },
                    { point: { x:270, y:350 }, kind: 'GoldCoin' },
                    { point: { x:100, y:300 }, kind: 'GoldCoin' },
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'GoldCoin' }
                ]
            }
        ],
        backdropName: 'Stone1'
    },
    /* Level 2 */ {
        blades: [
            { point: { x:320 / 2, y:480 / 3 }, texture: 'Falchion' },
            { point: { x:320 / 2, y:2 * 480 / 3 }, texture: 'Falchion' }
        ],
        waves: [
            /* Wave 1 */ {
                gems: [
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'Topaz' },
                    { point: { x:120, y:250 }, kind: 'GoldCoin' }
                ]
            },
            /* Wave 2 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'GoldCoin' },
                    { point: { x:75, y:150 }, kind: 'Topaz' },
                    { point: { x:270, y:350 }, kind: 'Topaz' },
                    { point: { x:100, y:300 }, kind: 'GoldCoin' }
                ],
            },
            /* Wave 3 */ {
                gems: [
                    { point: { x:75, y:250 }, kind: 'GoldCoin' },
                    { point: { x:200, y:150 }, kind: 'GoldCoin' },
                    { point: { x:100, y:350 }, kind: 'GoldCoin' },
                    { point: { x:300, y:300 }, kind: 'Topaz' },
                    { point: { x:300, y:100 }, kind: 'Topaz' }
                ]
            },
            /* Wave 4 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'Topaz' },
                    { point: { x:75, y:150 }, kind: 'GoldCoin' },
                    { point: { x:270, y:350 }, kind: 'GoldCoin' },
                    { point: { x:100, y:300 }, kind: 'Topaz' },
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'Topaz' }
                ]
            }
        ],
        backdropName: 'Sand1'
    },
    /* Level 3 */ {
        blades: [
            { point: { x:320 / 3, y:480 / 3 }, texture: 'Falchion' },
            { point: { x:2 * 320 / 3, y:480 / 3 }, texture: 'Falchion' },
            { point: { x:320 / 2, y:2 * 480 / 3 }, texture: 'Falchion' },
        ],
        waves: [
            /* Wave 1 */ {
                gems: [
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'Topaz' },
                    { point: { x:120, y:250 }, kind: 'GoldCoin' }
                ]
            },
            /* Wave 2 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'GoldCoin' },
                    { point: { x:75, y:150 }, kind: 'Topaz' },
                    { point: { x:270, y:350 }, kind: 'Topaz' },
                    { point: { x:100, y:300 }, kind: 'Amethyst' }
                ],
            },
            /* Wave 3 */ {
                gems: [
                    { point: { x:75, y:250 }, kind: 'GoldCoin' },
                    { point: { x:200, y:150 }, kind: 'Amethyst' },
                    { point: { x:100, y:350 }, kind: 'GoldCoin' },
                    { point: { x:300, y:300 }, kind: 'Topaz' },
                    { point: { x:300, y:100 }, kind: 'Topaz' }
                ]
            },
            /* Wave 4 */ {
                gems: [
                    { point: { x:20, y:250 }, kind: 'Topaz' },
                    { point: { x:75, y:150 }, kind: 'Amethyst' },
                    { point: { x:270, y:350 }, kind: 'Amethyst' },
                    { point: { x:100, y:300 }, kind: 'Topaz' },
                    { point: { x:250, y:150 }, kind: 'GoldCoin' },
                    { point: { x:220, y:350 }, kind: 'Topaz' }
                ]
            },
            /* Wave 5 */ {
                gems: [
                    { point: { x:50, y:280 }, kind: 'Topaz' },
                    { point: { x:75, y:130 }, kind: 'Amethyst' },
                    { point: { x:270, y:250 }, kind: 'Amethyst' },
                    { point: { x:50, y:330 }, kind: 'Topaz' },
                    { point: { x:220, y:190 }, kind: 'GoldCoin' },
                    { point: { x:220, y:280 }, kind: 'Topaz' }
                ]
            }
        ],
        backdropName: 'Sand1'
    }
];

function NewGame()
{
    levelNumber = 1;
    ReplayLevel();
}

function LevelLost()
{
    StopTimers();
    level.RemoveFromContainer();
    levelLost.className = 'dialog' + level.backdropName;
    window.setTimeout(function() { levelLost.style.display = 'block'; }, .25);
}

function ReplayLevel()
{
    levelLost.style.display = 'none';
	level = LoadLevel(levels[levelNumber - 1]);
    level.AddToContainer();
	StartTimers();
}

function LevelWon()
{
    if (levelNumber == levels.length)
        GameWon();
    else if (levelNumber == 1){
        StopTimers();
        level1Won.className = 'dialog' + level.backdropName;
        window.setTimeout(function() { level.RemoveFromContainer(); level1Won.style.display = 'block'; }, 500);
    }
	else {
        StopTimers();
        level2Won.className = 'dialog' + level.backdropName;
        window.setTimeout(function() { level.RemoveFromContainer(); level2Won.style.display = 'block'; }, 500);
	}
}

function NextLevel()
{
    ++levelNumber;
    level1Won.style.display = 'none';
    level2Won.style.display = 'none';
    ReplayLevel();
}

function GameWon()
{
    StopTimers();
    level.RemoveFromContainer();
    gameWon.className = 'dialog' + level.backdropName;
    window.setTimeout(function() { level.RemoveFromContainer(); gameWon.style.display = 'block'; }, 500);
}

function LaunchAppstore()
{
    window.open('http://itunes.apple.com/us/app/hand-of-greed-dodge-the-blade/id367911355?mt=8');
}

function StartTimers()
{ timer = window.setInterval(function() { if (level) level.CheckTouch(); }, 30); }

function StopTimers()
{
	window.clearInterval(timer);
    timer = null;
}

var level = null;
var levelNumber = 1;
var timer = null;
var imagesLoaded = false;

function Init()
{
	if (navigator.appVersion.indexOf('iPhone OS ') < 0 && navigator.appVersion.indexOf('iPad') < 0) {
		document.getElementById('game').style.display = 'none';
		document.getElementById('desktop').style.display = 'block';
	} else {
		if (!window.navigator.standalone && navigator.appVersion.indexOf('iPad') < 0) {
			document.getElementById('game').style.display = 'none';
			document.getElementById('install').style.display = 'block';
		} else {
			window.onorientationchange = UpdateOrientation;
			loader = new ImageLoader(function() { imagesLoaded = true; window.setInterval(function() { DismissSplash(); }, 3000) });
			loader.setImageUrls(requiredImages);
			loader.startLoading();
            InitializeGame();
		}
	}
}

function InitializeGame()
{
    welcome = document.getElementById('welcome');
    level1Won = document.getElementById('level1Won');
    level2Won = document.getElementById('level2Won');
    levelLost = document.getElementById('levelLost');
    gameWon = document.getElementById('gameWon');
    landscape = document.getElementById('landscape');
	game = document.getElementById('game'); 
    healthOrbBlood = document.getElementById('healthOrbBlood');
    gems = document.getElementById('gems');
    hazards = document.getElementById('hazards');
    blood = document.getElementById('blood');
    splash = document.getElementById('splash');

	game.ontouchstart = TouchStart;
	game.ontouchmove = TouchMove;
	game.ontouchend = TouchEnd;
}

function DismissSplash()
{
    if (imagesLoaded)
        splash.style.display = 'none';
}

function DismissWelcome()
{
    welcome.style.display = 'none';
    NewGame();
}
	
function TouchStart(event)
{
	event.preventDefault();
    if (level) level.TouchBegan(event.changedTouches[0]);
}

function TouchMove(event)
{
	event.preventDefault();
}

function TouchEnd(event)
{
	event.preventDefault();
    if (level)
        for (i = 0; i < event.changedTouches.length; ++i)
            level.TouchEnded(event.changedTouches[i]);
}

function UpdateOrientation(event)
{
	if (navigator.appVersion.indexOf('iPad') < 0)
		switch (window.orientation) {
			case -90:			
            case 90:
                landscape.style.display = 'block';
				break;
            default:
			case 0:
                landscape.style.display = 'none';
                window.scrollTo(0, 0);
				break;
		}
	else
		switch (window.orientation) {	
			case 0:
				document.getElementById('all').style.webkitTransform = 'scale(1.6) translateY(152px)';
				break;
			case 180:
				document.getElementById('all').style.webkitTransform = 'scale(1.6) translateY(152px)';
				break;
			case 90:
				document.getElementById('all').style.webkitTransform = 'scale(2) translate(16px, 22px)';
				break;
			case -90:
				document.getElementById('all').style.webkitTransform = 'scale(2) translate(16px, 22px)';
				break;
		}		
}

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

requiredImages =
[
    "BronzeBladeAxle.png",
    "BronzeBladeAxleShadow.png",
    "Cross.png",
    "Falchion.png",
    "FalchionShadow.png",
    "GoldCoin.png",
    "Hand.png",
    "HealthOrbBlood0.png",
    "HealthOrbBlood1.png",
    "HealthOrbBlood2.png",
    "HealthOrbBlood3.png",
    "HealthOrbEmpty.png",
    "OldPaperDialogStone.jpg",
    "OldPaperDialogSand.jpg",
    "MainMenuTitle.png",
    "Play.png",
    "Scimitar.png",
    "ScimitarShadow.png",
    "Sand1.jpg",
    "Stone1.jpg",
    "Topaz.png",
    "Amethyst.png"
];

function ImageLoader(callback)
{
    this.m_callback     = callback;
    this.m_imagesLoaded = 0;
    this.m_imageUrls    = [];
    this.m_images       = [];
}

ImageLoader.prototype.setImageUrls = function(imageUrls)
{
    this.m_imagesLoaded = 0;
    this.m_imageUrls    = imageUrls;
    this.m_images       = new Array(imageUrls.length);
}

ImageLoader.prototype.imageUrls = function() { return this.m_imageUrls; }

ImageLoader.prototype.startLoading = function()
{   
    for (var i = 0; i < this.m_imageUrls.length; ++i) {
        var image = new Image();
        
        image.onload = bind(function(image, i) {
            this._imageLoaded(image, i);
        }, this, [image, i]);

        image.src = 'images/' + this.m_imageUrls[i];
    }
}

ImageLoader.prototype._imageLoaded = function(image, index)
{
    this.m_images[index] = image;

    ++this.m_imagesLoaded;
    	    
    if (this.m_imagesLoaded == this.m_imageUrls.length)
        this.m_callback(this.m_images);
}

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

function bind(method, object, args) { return function() { return method.apply(object, args); } }
