var g_maxImg=40,
g_imglist=[],
g_doc=document,
g_rnd=Math.random,
g_frameDelay=50,
g_deadCannon,
g_frame,
g_score,
g_generateTargets=0,
g_paused=0,
g_targetList=0,
g_cityList=0,
g_cityListLen=0,
g_idling=0,
g_active,
g_none=-1;

var imgpre='';
var imggrid={
    lores: [
        [ 5,  5, 'b'],   // boom
        [71, 22, 'c'],   // city
        [71, 22, 'd'],   // dead city
        [ 7, 14, 'm'],   // missile
        [30, 13, 't'],   // target
        [71, 14, 'm'],   // launcher (cannon)
        [ 5,  5, 'g']    // game over banner
    ]
    ,
    hires: [
        [ 5,  5, 'b'],
        [71, 22, 'c'],
        [71, 22, 'd'],
        [38, 50, 'm'],
        [54, 13, 't'],
        [71, 14, 'l'],
        [ 5,  5, 'g']
    ]
};

// utility functions
function setimg(t,s)
{
    t.imgnum=s;
    var i = imggrid[imgpre][s];
    t.obj.width = i[0];
    t.obj.height = i[1];
    s = i[2];
    t.obj.src = imgpre + '/' + s + '.gif';
}

function getimg(t,s)
{
    t.id = g_none;

    // find a free slot in the imglist
    for (var i in g_imglist)
        if (g_imglist[i]==g_none) {
            t.id = i;
            break;
        }

    // if we didn't find one to re-use, add on to the end of the list
    if (t.id==g_none)
        if (g_imglist.length<g_maxImg)
            t.id = g_imglist.length;
        else
            return 0; // Cannot allocate new img

    // set up this img item
    t.obj = g_doc.getElementById('img.'+t.id);
    setimg(t,s);
    t.dead = 0;
    return 1;
}

function setrad(t, myFrame, halfFrame, x, y)
{
    var d=halfFrame*halfFrame - (myFrame-halfFrame)*(myFrame-halfFrame);
    var myobj = t.obj;
    myobj.width=d*x;
    myobj.height=d*y;
    myobj.style.top=t.y-(myobj.height/2)+'px';
    myobj.style.left=t.x-(myobj.width/2)+'px';
    show(t,1);
    return t.obj.height;
}

function show(t,b)
{
    if (!t) t=this;
    t.obj.style.visibility=b?'visible':'hidden';
    if (!b) g_imglist[t.id]=g_none;
}

function interp()
{
    var t=this;
    var param = (g_frame-t.startFrame) / (t.endFrame-t.startFrame);
    if (param>1) param=1;
    t.x = (t.endX-t.startX)*param + t.startX;
    t.y = (t.endY-t.startY)*param + t.startY;
    var myobj = t.obj;
    myobj.style.left = t.x - (myobj.width / 2)  +'px';
    myobj.style.top  = t.y - (myobj.height / 2) +'px';
    show(t, 1);
    if (param >= 1) t.draw = t.onArrival;
}

function setInterp(t, startX, startY, endX, endY, frameCount, onArrival)
{
    t.x=startX;
    t.y=startY;
    t.startX=startX;
    t.startY=startY;
    t.endX=endX;
    t.endY=endY;
    t.startFrame = g_frame;
    t.endFrame = g_frame + frameCount;
    t.draw = interp;
    t.onArrival = onArrival;
}

function none() {}

///// city object
function city_die()
{
    var t=this;
    if (!t.dead)
    {
        t.dead=1;
        setimg(t,2);  // imgnum 2 is city

        if (--g_cityListLen < 1) {
            new GameOverBanner();
        }
        else {
            // remove from linked list of cities
            if (t.next) t.next.prev = t.prev;
            if (t.prev) t.prev.next = t.next;
            if (g_cityList==t) g_cityList = t.next;
        }

        if(t.isTheCannon && !g_deadCannon)
        {
            show(t,0);
            g_deadCannon=1;  // dead cannon
        }
    }
}

function setGT()
{
    g_generateTargets=1;   // start generating targets
    this.draw=none;
    //window.status = "";
}

function City(x,y,isTheCannon)
{
    var t=this;

    // methods
    t.die=city_die;

    // members
    setInterp(t, x, 0, x, y, 20, setGT);
    t.isTheCannon=isTheCannon;

    // add to linked list
    t.next = g_cityList;
    t.prev = 0;
    if (g_cityList) g_cityList.prev = t;
    g_cityList = t;

    // get img
    if(t.isTheCannon) getimg(t,5);    // imgnum 5 is 'launcher' (cannon)
    else getimg(t,1);           // imgnum 1 is 'city'
    this.draw();
    show(t,1);

    // register
    g_imglist[t.id]=t;
    g_cityListLen++;  // increment city count
}


///// target object -- incoming alien bad guy
function target_die()
{
    var t=this;

    show(t,0);
    new Boom(t.x, t.y);
    g_score+=g_cityListLen;  // win points for target dying (one point for each city)

    // remove from linked list of targets
    if (t.next) t.next.prev = t.prev;
    if (t.prev) t.prev.next = t.next;
    if (g_targetList==t) g_targetList = t.next;
}

function Target()
{
    var t=this;

    // methods
    t.die=target_die;

    // get img
    if (getimg(t,4) && g_cityListLen>0)   // imgnum 4 is 'target'
    {
        // pick a target
        var c=g_rnd()*g_cityListLen;
        var i=g_cityList;
        for(; c>=1; i=i.next) --c;

        // head towards target
        setInterp(t, g_rnd()*600, 0, i.x, i.y,
                g_rnd()*80+60-g_frame*.05, target_die);

        // add to linked list
        t.next = g_targetList;
        t.prev = 0;
        if (g_targetList) g_targetList.prev = t;
        g_targetList = t;

        // register
        g_imglist[t.id]=t;
    }
}

///// missile object
function missile_die()
{
    show(this, 0);
    new Boom(this.x,this.y);
}

function Missile(x,y)
{
    var t=this;

    // methods
    t.die=missile_die;

    // get img
    // don't launch if not gen targets
    // See if the cannon is dead
    if(g_generateTargets && !g_deadCannon && getimg(t,3))   // imgnum 3 is 'missile'
    {
        // members
        var startX = 340;
        var startY = 427;
        setInterp(t, startX, startY, x, y,
            Math.sqrt((startX-x)*(startX-x) + (startY-y)*(startY-y))*.05,
            missile_die);

        // register
        g_imglist[t.id]=t;
        g_score--;  // lose a point for firing a missile
    }
}


///// boom object (explosions)
function boom_draw()
{
    var t=this;
    var myFrame=g_frame-t.startFrame;
    var halfFrame=13;  // frame count for half-explosion
    var d=setrad(t, myFrame, halfFrame, .5, .5);
    var xd, yd, i;

    // collision detect
    var r2=d*d/4;
    for (i = g_targetList; i; i=i.next)
    {
        xd=i.x-t.x;
        yd=i.y-t.y;
        if (xd*xd+yd*yd < r2) i.die();
    }
    if (!t.ck && myFrame >= halfFrame)
    {
        t.ck = 1;
        for (i = g_cityList; i; i=i.next)
        {
            xd=i.x-t.x;
            yd=i.y-t.y;
            if (xd*xd+yd*yd < r2) i.die();
        }
    }

    if (myFrame>halfFrame*2) show(t,0);
}

function Boom(x,y)
{
    var t=this;

    // methods
    t.draw=boom_draw;

    // get img
    if (getimg(t,0))  // imgnum 0 is 'boom'
    {
        // members
        t.x=x;
        t.y=y;
        t.startFrame=g_frame;
        t.ck=0;   // killed any cities yet?

        // register
        g_imglist[t.id]=t;
    }
}

///// game over object (GameOverBanner)
function gameOver_draw()
{
    var t=this;
    var myFrame=g_frame-t.startFrame;
    var dur=20;  // duration of grow
    if (myFrame>dur) myFrame=dur;
    setrad(t, myFrame, dur, 1.4, .2);
}

function GameOverBanner()
{
    var t=this;

    // methods
    t.draw=gameOver_draw;

    // get img
    if (getimg(t,6))  // imgnum 6 is a game over banner
    {
        // members
        t.startFrame=g_frame;
        t.dead=1;
        t.x=350;
        t.y=200;

        // register
        g_imglist[t.id]=t;
    }
}


///// score object
function score_draw()
{
    if (this.ent.value != g_score) this.ent.value=g_score;
}

function Score()
{
    var t=this;
    t.obj=g_doc.getElementById("sform");
    t.obj.width=300;
    t.obj.height=50;
    t.ent=g_doc.getElementById("sbox");
    setInterp(t, 300, 0, 300, 500, 20, score_draw);
    t.dead=1;
    t.imgnum=0;
    t.id=0;
    g_imglist[t.id] = t;
    show(t,1);
}


///// main functions
var synchErr=0, synchA=0, synchB=0;
var nextTargetFrame=0;
function idle()
{
    // set g_idling to zero to stop the event loop
    // NOTE: Use g_paused for pausing, not g_idling
    if (!g_idling) return;

    // Try to detect slow runtime engine
    synchA=g_frame-g_frameDelay/50;
    if (synchA > synchB) {
        //if (!synchErr) window.status = "SYNCH";
        synchErr=1;
    }
    if (!synchErr) setTimeout('idle()',g_frameDelay);

    if (!g_paused) // skip if game is paused
    {
        // draw cities, missiles, and booms
        var deadcount=0;
        g_active=0;
        for (var i in g_imglist)
            if (g_imglist[i]!=g_none) {
                ++g_active;
                deadcount += g_imglist[i].dead;
                g_imglist[i].draw();
            }
        if (deadcount >= g_active) {
            if (!g_active) {
                g_idling = 0;  // everything's dead and gone
                return;
            }
            g_generateTargets=0;   // stop generating targets
            for (var j in g_imglist)
                if (g_imglist[j]!=g_none) {
                    g_imglist[j].dead = 0;
                    setInterp(g_imglist[j],
                        g_imglist[j].x, g_imglist[j].y,
                        g_imglist[j].x, -20,
                        50, show);
                }
        }

        // generate targets
        if (g_generateTargets && g_frame >= nextTargetFrame)
        {
            new Target();
            nextTargetFrame = g_frame+(g_rnd()*30);
        }

        // increment frame
        g_frame += g_frameDelay / 50;
    }

    if (synchErr) setTimeout('idle()',60);
    else setTimeout('synchB=' + g_frame,5);
}

function click(e)
{
    if (!g_paused)  // skip if game is paused
    {
        if (e) new Missile(e.clientX,e.clientY);
        else new Missile(event.x,event.y);
    }
}

function set_hires(t)
{
    imgpre=t?'hires':'lores';
    for (var i in g_imglist)
        if (g_imglist[i] != g_none && g_imglist[i].imgnum)
            setimg(g_imglist[i], g_imglist[i].imgnum);
}

var typed='';
function dokey(e)
{
    var key = e ? e.which : event.keyCode;
    key = String.fromCharCode(key);
    //window.status = "active: " + g_active + ", key: " + key;
    if (g_active)
    {
        if (key == 'P') g_paused = !g_paused;  // toggle paused
        if (key == 'H') set_hires(1);
        if (key == 'L') set_hires(0);
    }
    else
    {
        typed += key;
        typed = typed.substring(typed.length-6);
        if (typed == 'KILLEM')
        {
            set_hires(0);
            startgame();
        }
/* commented out the following if block for the5k contest */
        if (typed == 'MATROX')
        {
            set_hires(1);
            startgame();
        }
    }
}

function startgame()
{
    g_deadCannon=0;      // long live the cannon
    g_score=0;
    g_frame=0;
    g_doc.onmousedown=click;
    new Score();
    new City( 80,450,0);
    new City(210,450,0);
    new City(340,450,0);
    new City(340,427,1); // the cannon
    new City(470,450,0);
    new City(600,450,0);
    if (!g_idling) {
        g_idling = 1;
        idle();
    }
}

function afterload(go, type)
{
    g_doc.onkeydown = dokey;
    if (go) {
        set_hires(type||0);
        startgame();
    }
}

/*
function foo() {
    var str = '';
    for (var i in g_imglist) {
        str += i + ": " + g_imglist[i] + "\n";
    }
    alert (str);
}
*/

function gamehtml()
{
    var s=' style="position:absolute;visibility:hidden"';
    for (var i=0; i<g_maxImg; ++i)
    {
        g_doc.write('<img id="img.' + i + '"' + s + ' />');
    }
    g_doc.write('<form id="sform"'+s+'>Score: <input id="sbox" size="10"></form>');
}

gamehtml();