// Solitaire for HTML5
// tepaanan@gmail.com

// Mouse pos
var mousex=0, mousey=0;

// Mouse click and card's upper left corner offset
var xOff=0, yOff=0;

// Mouse offset between moves
var moveXAmount=0;
var moveYAmount=0;

// Card size
var cardHeight=110;
var cardWidth=75;

// Canvas
var canvas
var ctx;
var tmpCanvas;
var tmpCtx;

// Is mouse down
var mouseDown=false;

// For cards
var activeCard=undefined;
var cardLoadingIndex=0;
var cardArray = new Array();
var cardNameArray = new Array();

// For decks
var deck1 = new DeckObject(1,0,0);
var deck2 = new DeckObject(2,0,0);
var deck3 = new DeckObject(3,0,0);
var deck4 = new DeckObject(4,0,0);
var deck5 = new DeckObject(5,0,0);
var deck6 = new DeckObject(6,0,0);
var deck7 = new DeckObject(7,0,0);

// Background
var background = new Image();

// Loading graphics -message
var message;

// Window resize event
var resizeTimer = undefined;
var documentWidth = 0;
var documentHeight = 0;


/******************************************************************************
 * Card object
 * http://www.javascriptkit.com/javatutors/object2.shtml
 */ 
function CardObject(id, x, y, img){
    // Properties
    this.id = id;
    this.x = x;
    this.y = y;
    this.oldX = x;
    this.oldY = y;
    this.img = img;
    // Methods
    this.cancel = _CardObject_cancel;
    this.storePos = _CardObject_storePos;
}
function _CardObject_storePos(x,y) {
    this.x = x;
    this.y = y;
    this.oldX = x;
    this.oldY = y;
}
function _CardObject_cancel() {
    this.x = this.oldX;
    this.y = this.oldY;
}


/******************************************************************************
 * Deck object
 */ 
function DeckObject(id, x, y){
    // Properties
    this.id = id;
    this.x = x;
    this.y = y;
    this.cardArray = new Array();
    // Methods
    this.setPos = _DeckObject_setPos;
    this.addCard = _DeckObject_addCard;
    this.cardUnderMouse = _DeckObject_cardUnderMouse;
    this.drawDeck = _DeckObject_drawDeck;
}
function _DeckObject_drawDeck(ctx) {
    for(var i=0 ; i < this.cardArray.length ; i++) {
        var card = this.cardArray[i];
        if (card!=activeCard)
            ctx.drawImage(card.img,card.x,card.y,cardWidth,cardHeight);
    }
}
function _DeckObject_setPos(x,y){
    this.x = x;
    this.y = y;
}
function _DeckObject_addCard(card){
    card.storePos(this.x,this.y + 20 * this.cardArray.length);
    this.cardArray.push(card);
}
function _DeckObject_cardUnderMouse(x,y){
    for(var i=this.cardArray.length-1;i>=0;i--) {
        var card = this.cardArray[i];
        if (card.x < x && card.y < y && card.x+cardWidth > x && card.y+cardHeight > y) {
            return card;
        }
    }
    return undefined;
}


/******************************************************************************
 * Clear canvas
 * TODO: Not in use
 */ 
function clearCanvas(x,y,w,h) {
    // Whole canvas
    ctx.clearRect(0,0,canvas.width,canvas.height);
}


/******************************************************************************
 * Window resize event handling
 */ 
function resizeEvent(event) {
    if(resizeTimer) {
        clearTimeout(resizeTimer);
        resizeTimer = undefined;
    }
    resizeTimer = setTimeout("doResize()",100); 
}
function doResize() {
    resizeTimer = undefined;
    // TODO: is that good solution?
    window.location.reload();
}


/******************************************************************************
 * Draw fast single card
 * Draw only dirty background behind the card
 * Draw active moving card on top of all others
 */ 
function drawCardFast(cardToDraw, toContext, toBackground) {
    // Draw dirty background behind the card
    toContext.drawImage(toBackground, cardToDraw.x-moveXAmount, cardToDraw.y-moveYAmount, cardWidth+moveXAmount*2, cardHeight+moveYAmount*2, 
        cardToDraw.x-moveXAmount, cardToDraw.y-moveYAmount, cardWidth+moveXAmount*2, cardHeight+moveYAmount*2 );
    // Draw card
    toContext.drawImage(cardToDraw.img,cardToDraw.x,cardToDraw.y,cardWidth,cardHeight);
}

/******************************************************************************
 * Copy whole canvas and draw it once
 * Starts showing copy of the original canvas
 */ 
function copyCanvasAndDraw() {
    // Draw target canvas to ready for copying it
    ctx.drawImage(background,0,0,documentWidth,documentHeight);

    // Draw decks
    deck1.drawDeck(ctx);
    deck2.drawDeck(ctx);
    deck3.drawDeck(ctx);
    deck4.drawDeck(ctx);
    deck5.drawDeck(ctx);
    deck6.drawDeck(ctx);
    deck7.drawDeck(ctx);

    // Draw target canvas to temporary canvas
    tmpCtx.drawImage(canvas, 0, 0);

    // And active card on that
    if (activeCard) {
        tmpCtx.drawImage(activeCard.img,activeCard.x,activeCard.y,cardWidth,cardHeight);
    }

    // Change temporary canvas to visible
    canvas.style.display="none";
    tmpCanvas.style.display="block";
}

/******************************************************************************
 * Draw copied main canvas and active card that is user moving 
 */ 
function drawCopiedCanvasAndCard() {
    // Fast card drawing
    drawCardFast(activeCard,tmpCtx,canvas);
}

/******************************************************************************
 * Set original canvas back to visible 
 */ 
function resetCanvas() {
    canvas.style.display="block";
    tmpCanvas.style.display="none";
}

/******************************************************************************
 * Draw all
 */ 
function drawAll() {
    // Background
    ctx.drawImage(background,0,0,documentWidth,documentHeight);

    // Draw decks
    deck1.drawDeck(ctx);
    deck2.drawDeck(ctx);
    deck3.drawDeck(ctx);
    deck4.drawDeck(ctx);
    deck5.drawDeck(ctx);
    deck6.drawDeck(ctx);
    deck7.drawDeck(ctx);

    // Draw active card lates to get it on top of all rest
    if (activeCard) {
        ctx.drawImage(activeCard.img,activeCard.x,activeCard.y,cardWidth,cardHeight);
    }
}

/******************************************************************************
 * Initialize cards and load card graphics
 */ 
function initCards(index) {
    updateMessage('Downloading graphics... '+cardLoadingIndex+'/' + cardNameArray.length); 

    if (cardNameArray.length < index) {
        message.style.display='none';

        // All loaded
        initDecks();
        return;
    }

    // Create card object    
    var card = new CardObject(1,0,0,undefined);
    card.img = new Image();
    card.img.onload = function(){
        initCards(cardLoadingIndex+1);
    };
    card.img.src = cardNameArray[cardLoadingIndex];
    cardArray.push(card);
    cardLoadingIndex++; 
}

/******************************************************************************
 * Create all game decks
 */ 
function initDecks() {

    // TODO: Mess cards first

    // Deal cards into decks
    var capX = documentWidth / 9 / 4;
    var index = 0;
    deck1.setPos(capX,200);
    deck1.addCard(cardArray[index++]);

    deck2.setPos(cardWidth*1+capX*2,200);
    deck2.addCard(cardArray[index++]);
    deck2.addCard(cardArray[index++]);

    deck3.setPos(cardWidth*2+capX*3,200);
    deck3.addCard(cardArray[index++]);
    deck3.addCard(cardArray[index++]);
    deck3.addCard(cardArray[index++]);

    deck4.setPos(cardWidth*3+capX*4,200);
    deck4.addCard(cardArray[index++]);
    deck4.addCard(cardArray[index++]);
    deck4.addCard(cardArray[index++]);
    deck4.addCard(cardArray[index++]);

    deck5.setPos(cardWidth*4+capX*5,200);
    deck5.addCard(cardArray[index++]);
    deck5.addCard(cardArray[index++]);
    deck5.addCard(cardArray[index++]);
    deck5.addCard(cardArray[index++]);
    deck5.addCard(cardArray[index++]);

    deck6.setPos(cardWidth*5+capX*6,200);
    deck6.addCard(cardArray[index++]);
    deck6.addCard(cardArray[index++]);
    deck6.addCard(cardArray[index++]);
    deck6.addCard(cardArray[index++]);
    deck6.addCard(cardArray[index++]);
    deck6.addCard(cardArray[index++]);

    deck7.setPos(cardWidth*6+capX*7,200);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);
    deck7.addCard(cardArray[index++]);


    // All created
    drawAll();
}

/******************************************************************************
 * Update message to the user
 */ 
function updateMessage(text) {
    message.innerHTML = text;
}


/******************************************************************************
 * Initialize canvas for drawing and load graphics
 */ 
function init() {
    // Document size
    documentWidth = document.body.offsetWidth;
    documentHeight = document.body.offsetHeight;

    // Card size
    cardHeight=documentWidth / 6.5;
    cardWidth=cardHeight / 1.46;

    // Main canvas for drawing surface
    canvas = document.getElementById('deskCanvas');
    canvas.width = documentWidth - canvas.offsetLeft*2;
    canvas.height = documentHeight - canvas.offsetTop;
    canvas.style.display="block";
    ctx = document.getElementById('deskCanvas').getContext('2d');


    // Add the temporary canvas where main canvas is copied/drawed in mousedown
    // Temporary canvas is drawed in mousemove
    var container = canvas.parentNode;
    tmpCanvas = document.createElement('canvas');
    tmpCanvas.style.display="none";
    tmpCanvas.id     = 'tmpDeskCanvas';
    tmpCanvas.width  = canvas.width;
    tmpCanvas.height = canvas.height;
    tmpCanvas.offsetLeft = canvas.offsetLeft;
    tmpCanvas.offsetRight = canvas.offsetRight;
    container.appendChild(tmpCanvas);
    tmpCtx = tmpCanvas.getContext('2d');    
        
    // Loading graphics message
    message = document.getElementById('message');

    // Mouse events
    canvas.addEventListener('mousedown', ev_mousedown, false);
    canvas.addEventListener('mousemove', ev_mousemove, false);
    tmpCanvas.addEventListener('mousemove', ev_mousemove, false);
    canvas.addEventListener('mouseup',   ev_mouseup, false);
    tmpCanvas.addEventListener('mouseup',   ev_mouseup, false);

    // TODO:For iOS
    //canvas.addEventListener( 'touchstart', ev_mousedown, false);
    //canvas.addEventListener( 'touchmove', ev_mousemove, false);
    //canvas.addEventListener( 'touchend', ev_mouseup, false );    

    // TODO: Load graphics only once if possible?

    // Card names
    cardLoadingIndex = 0;
    cardNameArray.push('gfx/Club_ace.png');
    cardNameArray.push('gfx/Club_2.png');
    cardNameArray.push('gfx/Club_3.png');
    cardNameArray.push('gfx/Club_4.png');
    cardNameArray.push('gfx/Club_5.png');
    cardNameArray.push('gfx/Club_6.png');
    cardNameArray.push('gfx/Club_7.png');
    cardNameArray.push('gfx/Club_8.png');
    cardNameArray.push('gfx/Club_9.png');
    cardNameArray.push('gfx/Club_10.png');
    cardNameArray.push('gfx/Club_jack.png');
    cardNameArray.push('gfx/Club_king.png');
    cardNameArray.push('gfx/Club_queen.png');
    cardNameArray.push('gfx/Diamond_ace.png');
    cardNameArray.push('gfx/Diamond_2.png');
    cardNameArray.push('gfx/Diamond_3.png');
    cardNameArray.push('gfx/Diamond_4.png');
    cardNameArray.push('gfx/Diamond_5.png');
    cardNameArray.push('gfx/Diamond_6.png');
    cardNameArray.push('gfx/Diamond_7.png');
    cardNameArray.push('gfx/Diamond_8.png');
    cardNameArray.push('gfx/Diamond_9.png');
    cardNameArray.push('gfx/Diamond_10.png');
    cardNameArray.push('gfx/Diamond_jack.png');
    cardNameArray.push('gfx/Diamond_king.png');
    cardNameArray.push('gfx/Diamond_queen.png');
    cardNameArray.push('gfx/Heart_ace.png');
    cardNameArray.push('gfx/Heart_2.png');
    cardNameArray.push('gfx/Heart_3.png');
    cardNameArray.push('gfx/Heart_4.png');
    cardNameArray.push('gfx/Heart_5.png');
    cardNameArray.push('gfx/Heart_6.png');
    cardNameArray.push('gfx/Heart_7.png');
    cardNameArray.push('gfx/Heart_8.png');
    cardNameArray.push('gfx/Heart_9.png');
    cardNameArray.push('gfx/Heart_10.png');
    cardNameArray.push('gfx/Heart_jack.png');
    cardNameArray.push('gfx/Heart_king.png');
    cardNameArray.push('gfx/Heart_queen.png');
    cardNameArray.push('gfx/Spade_ace.png');
    cardNameArray.push('gfx/Spade_2.png');
    cardNameArray.push('gfx/Spade_3.png');
    cardNameArray.push('gfx/Spade_4.png');
    cardNameArray.push('gfx/Spade_5.png');
    cardNameArray.push('gfx/Spade_6.png');
    cardNameArray.push('gfx/Spade_7.png');
    cardNameArray.push('gfx/Spade_8.png');
    cardNameArray.push('gfx/Spade_9.png');
    cardNameArray.push('gfx/Spade_10.png');
    cardNameArray.push('gfx/Spade_jack.png');
    cardNameArray.push('gfx/Spade_king.png');
    cardNameArray.push('gfx/Spade_queen.png');
    // Background
    background.onload  = function(){
        // When background is loaded start loading cards
        initCards(cardLoadingIndex);
    }; 
    background.src = 'gfx/background.png';
}

/******************************************************************************
 * Find card under mouse coordinates
 */ 
function cardUnderMouse(x,y) {
    // Find deck under user mouse press
    var card = undefined;

    if(card = deck1.cardUnderMouse(x,y)) return card;
    if(card = deck2.cardUnderMouse(x,y)) return card;
    if(card = deck3.cardUnderMouse(x,y)) return card;
    if(card = deck4.cardUnderMouse(x,y)) return card;
    if(card = deck5.cardUnderMouse(x,y)) return card;
    if(card = deck6.cardUnderMouse(x,y)) return card;
    if(card = deck7.cardUnderMouse(x,y)) return card;

    return undefined;
}

/******************************************************************************
 * Mouse down
 */ 
function ev_mousedown (ev) {
    mouseDown = true;
    mousex = ev.clientX - canvas.offsetLeft;
    mousey = ev.clientY - canvas.offsetTop;
        
    activeCard = cardUnderMouse(mousex,mousey);
    if (activeCard) {
        xOff = mousex - activeCard.x;
        yOff = mousey - activeCard.y;
        activeCard.x = mousex - xOff;
        activeCard.y = mousey - yOff;

        // Copy main canvas
        // Start drawing copy of it
        copyCanvasAndDraw();
    }            
}

/******************************************************************************
 * Mouse move
 */ 
function ev_mousemove (ev) {
    if (!mouseDown)
        return;

    var newX = ev.clientX - tmpCanvas.offsetLeft;
    var newY = ev.clientY - tmpCanvas.offsetTop;

    // Is mouse position outside canvas
    var tmpX = newX - xOff;
    var tmpY = newY - yOff;
    if (tmpX < 1 || 
        tmpX + cardWidth >= canvas.width ||
        tmpY < 1 ||
        tmpY + cardHeight >= canvas.height) {
        // MouseUp
        mouseDown = false;

        if (activeCard) {
            activeCard.cancel();
            activeCard = undefined;
        }

        // Draw whole main canvas
        drawAll();
        // Start showing again main canvas
        resetCanvas();
        return;
    }

    moveXAmount = Math.abs(mousex - newX) * 5;
    moveYAmount = Math.abs(mousey - newY) * 5;
    mousex = newX;
    mousey = newY;
       
    activeCard.x = mousex - xOff;
    activeCard.y = mousey - yOff;

    // Draw copied main canvas and active moving card
    drawCopiedCanvasAndCard();
}

/******************************************************************************
 * Mouse up
 */ 
function ev_mouseup (ev) {
    mouseDown = false;
    mousex=0;
    mousey=0;
    if (activeCard)
       activeCard.cancel();

    activeCard = undefined;

    // Draw whole main canvas
    drawAll();
    // Start showing again main canvas
    resetCanvas();
} 
