/* 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. 
 * 
 * This file is available and licensed under the following license:
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *
 *   * 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.
 *
 *   * Neither the name of Sun Microsystems nor the names of its contributors 
 *     may be used to endorse or promote products derived from this software 
 *     without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
 */
package shoppingservice;

import java.lang.Exception;
import java.lang.Object;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.io.http.HttpHeader;
import javafx.io.http.HttpRequest;
import javafx.io.http.URLConverter;
import javafx.lang.FX;
import javafx.scene.control.TextBox;
import javafx.scene.Group;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import shoppingservice.model.ProductCatalog;
import shoppingservice.parser.XMLPullParser;
import shoppingservice.view.ImageButton;
import shoppingservice.view.ListItem;
import shoppingservice.view.MessageView;
import shoppingservice.view.ProductInfoView;

// appid from http://developer.yahoo.com/shopping/
def appid = FX.getArgument("yahoo_appid");

// Application Bounds
def stageWidth = 240;
def stageHeight = 320;

// Product Details Index
var pageIndex:Integer = 0;

// Information about all relevant products
public var products: ProductCatalog[];

// data request handlers
public var onExceptionHandler = function(exception: Exception) {
    exception.printStackTrace();
    var message = exception.getMessage();
    if(message == null) { message = exception.toString(); }
    alert("Error: {message.trim()}");
}

public var onResponseCodeHandler = function(responseCode:Integer) {
    if (responseCode != 200) {
        alert("Error fetching results: {responseCode}");
    }
}

public var onInputHandler = function(input: java.io.InputStream) {
    try {
        XMLPullParser.processResults(input);
    } finally {
        input.close();
    }
}

public var onDoneHandler = function() {
    productInfoView.product = null;
    if((sizeof products) == 0) {
        alert("Your search - {searchText.text} - did not match any products.");
    } else {
        messageView.show = false;
        showProductList(0, false);
    }
}

// Search and load product details
function searchProducts(product:String) {
    
    delete products;
    productInfoView.show = false;
    productListVisible = false;
        
    // Perform some basic validation on product
    if(product.trim().length() <= 0) { 
        alert("Please enter a product to search in above text box.");
        return; 
    }
    
    var location = "http://shopping.yahooapis.com/ShoppingService/v3/productSearch?"
    "appid={appid}&query={URLConverter { }.encodeString(product.trim())}&show_numratings=0&results=12";
        
    println("Loading xml data from {location}");
    alert("Searching for product \"{product}\"  \nPlease wait...");
    
    try {
        var request = HttpRequest {
            location: location
            method: HttpRequest.GET
            onException: onExceptionHandler
            onResponseCode: onResponseCodeHandler
            onInput: onInputHandler
            onDone: onDoneHandler
        }
        request.setHeader(HttpHeader.USER_AGENT, "Mozilla/4.0");
        request.start();
    } catch (e:Exception) {
        println("WARNING: {e}");
        alert("Error: Could not search... Please try again later.");
    }
}

// Display details of product at specified page in list
public function showProductList(pageIndex:Integer, scrollLeft:Boolean) {
    
    productListVisible = true;
    productInfoView.show = false;
        
    if(products.size() < ((pageIndex-1) * 6)) { return ; }
    if((sizeof productListItem) == 0) { initProductListItem(); }
    
    var scrollXVal = 1; // Scroll Right
    if(scrollLeft) { scrollXVal = -1; }
    
    productListX = 0;
    
    // Slide product details animation
    var timeline:Timeline = Timeline {
       keyFrames: [ 
            KeyFrame {
                time: 250ms
                values: [ productListX => scrollXVal * stageWidth tween Interpolator.LINEAR ] 
                action: function() {
                    productListX = scrollXVal * -stageWidth;
                    for(i in [0..(productListItem.size() - 1)]) {
                        if((pageIndex + i) < products.size()) {
                            var product = products[(pageIndex * 6) + i];
                            productListItem[i].product = product;
                        } else {
                            productListItem[i].product = null;
                        }
                    }
                }
            },
            KeyFrame {
                time: 250ms
                values: [ productListX => scrollXVal * -stageWidth tween Interpolator.DISCRETE ] 
            },
            KeyFrame {
                time: 500ms
                values: [ productListX => 0 tween Interpolator.LINEAR ] 
            }
       ]
    };
    timeline.playFromStart();    
}

/***************************************
 * Application User Interface
 ***************************************/

public def defaultStarImage = Image { url: "{__DIR__}images/star0.png" };

// Background Image
var bgImage : ImageView = ImageView { 
    image: Image {
        url: "{__DIR__}images/background.png"
    }
    onKeyPressed:function(e:KeyEvent):Void {
        if(e.code == KeyCode.VK_LEFT) {
            onBack();
        } else if(e.code == KeyCode.VK_RIGHT) {
            onNext();
        } else if(e.code == KeyCode.VK_UP) {
            searchText.requestFocus();
        } else if(e.code == KeyCode.VK_ENTER) {
            productInfoView.reorderNodes();
        }
    }
}

var messageView = MessageView { };

// Display details of previous set of products in list
var backButton: ImageButton = ImageButton { 
    
    x: 25
    y: stageHeight - 25
    enabled: bind enableBackButton(
        products, pageIndex, productInfoView.product.index, productInfoView.visible)
        
    normalImage: Image { url: "{__DIR__}images/back_n.png" }
    overImage: Image { url: "{__DIR__}images/back_h.png" }
    disabledImage: Image { url: "{__DIR__}images/back_d.png" }
        
    onMouseClicked: function(e) {
        onBack();
    }
}

function onBack() : Void {
    
    if(not backButton.enabled) { return; }
    
    if(productInfoView.visible) {
        var prevIndex = productInfoView.product.index - 1;
        if(prevIndex < 0) { return; }
        productInfoView.product = products[prevIndex];
    } else {
        pageIndex--; // Decrement page
        // Display list of products for specified page
        showProductList(pageIndex, false);
    }
}

function enableBackButton(
    pc:ProductCatalog[], page:Integer, productIndex:Integer,
    productDetailsVisible:Boolean) : Boolean {
    if(productDetailsVisible) {
        return (productIndex > 0);
    } else {
        return (page > 0);
    }
}

// Display details of next set of products in list
var nextButton: ImageButton = ImageButton { 
    
    x: 60
    y: stageHeight - 25
    enabled: bind enableNextButton(
        products, pageIndex, productInfoView.product.index, productInfoView.visible)
    
    normalImage: Image { url: "{__DIR__}images/next_n.png" }
    overImage: Image { url: "{__DIR__}images/next_h.png" }
    disabledImage: Image { url: "{__DIR__}images/next_d.png" }
    
    onMouseClicked: function(e) {
        onNext();
    }
}

function onNext() : Void {
        
    if(not nextButton.enabled) { return; }
    
    if(productInfoView.visible) {
        var nextIndex = productInfoView.product.index + 1;
        if(nextIndex >= (sizeof products)) { return; }
        productInfoView.product = products[nextIndex];
    } else {
        pageIndex++; // Increment page
        // Display list of products for specified page
        showProductList(pageIndex, true); 
    }
}

function enableNextButton(
    pc:ProductCatalog[], page:Integer, productIndex:Integer, 
    productDetailsVisible:Boolean) : Boolean {
    if(productDetailsVisible) {
        return (productIndex < (pc.size() - 1));
    } else {
        return (pc.size() > ((page + 1) * 6));
    }
}

// Change Theme
var themeIndex = 1;
var themes: String[];
var themeImage = ImageView { 
    x: 2
    y: 38
    image: Image { url: "{__DIR__}images/theme_0.png" }
}

// Theme Bar
var themeBar : ImageView = ImageView {
    x: 2
    y: stageHeight - 5
    image: Image { url: "{__DIR__}images/theme_n.png" }
    opacity: 0.25
    onMouseEntered: function(e) { themeBar.opacity = 0.5; }
    onMouseExited: function(e) { themeBar.opacity = 0.25; }
    onMousePressed: function(e) {
        themeIndex++; 
        if(themeIndex >= themes.size()) { themeIndex = 0; }
        themeImage.image = Image { url: themes[themeIndex] };
    }
}

var listButton: ImageButton = ImageButton { 
    
    x: stageWidth - 37
    y: stageHeight - 25
    
    normalImage: Image { url: "{__DIR__}images/list_n.png" }
    overImage: Image { url: "{__DIR__}images/list_h.png" }
    
    onMousePressed: function(e) {
        productInfoView.show = false;
        productListVisible = true;
    }
}

// Show or Hide different Views based on context
var maxMinButton = ImageButton { 
    
    x: 112
    y: stageHeight - 25
    normalImage: Image { url: "{__DIR__}images/maxmin_n.png" }
    overImage: Image { url: "{__DIR__}images/maxmin_h.png" }
    
    onMouseClicked: function(e) {
        productInfoView.reorderNodes();
    }
}

// Dispose Application
var closeButton = ImageButton { 
    
    x: stageWidth - 20
    y: 13
    normalImage: Image { url: "{__DIR__}images/x_normal.png" }
    overImage: Image { url: "{__DIR__}images/x_over.png" }
    visible: bind ("{__PROFILE__}" != "browser")
    
    onMouseClicked: function(e) {
        javafx.lang.FX.exit();
    }
}

public var stageDragInitialX:Number;
public var stageDragInitialY:Number;

// Drag Bar
var dragBar = Rectangle {
    width: stageWidth
    height: stageHeight
    fill: Color.TRANSPARENT
    visible: bind ("{__PROFILE__}" != "browser")
    onMousePressed: function(e) {
        stageDragInitialX = e.screenX - stage.x;
        stageDragInitialY = e.screenY - stage.y;
    }
     onMouseDragged: function(e) {
        stage.x = e.screenX - stageDragInitialX;
        stage.y = e.screenY - stageDragInitialY;
     }
}

// Product List Items
var productListItem: ListItem[];
function initProductListItem() {
    for(i in [0..5]) {
        insert ListItem { translateX: 0 translateY: 38.5 + (i * ListItem.height) } into productListItem;
    };
}

// Product List
var productListX: Number = 0;
var productListVisible = true;
var productList = Group {
    visible: bind ((not productInfoView.visible) and productListVisible)
    content: bind [ productListItem ]
    translateX: bind productListX
    clip: Rectangle {
        x: 0
        y: 32
        width: stageWidth
        height: stageHeight - 64
    }
}

// Product Details View
public var productInfoView = ProductInfoView {};

// Search Text
var textBounds: Rectangle = Rectangle {
    x: 11 y: 8 width: 170 height: 23
};
var searchText: TextBox = TextBox {
    translateX: textBounds.x
    translateY: textBounds.y
    width: textBounds.width
    height: textBounds.height
    columns: 13
    selectOnFocus: false
    text: ""
    blocksMouse: true
    action: function() {
        searchProducts(searchText.text.trim());
    }
    onKeyPressed:function(e:KeyEvent):Void {
        if(e.code == KeyCode.VK_DOWN) {
            bgImage.requestFocus();
        }
    }
}

// Search for products with in range of specified ZipCode
var searchButton = ImageButton { 
    
    x: stageWidth - 52
    y: 7
    
    normalImage: Image { url: "{__DIR__}images/search_n.png" };
    overImage: Image { url: "{__DIR__}images/search_h.png" };
    
    onMouseClicked: function(e) {
        searchText.commit();
        searchProducts(searchText.text.trim());
    }
}

var innerBorderRect = Rectangle {
    x: 1 y: 1 width: stageWidth - 2 height: stageHeight - 2
    arcWidth: 8 arcHeight: 8
    stroke: Color.rgb(30, 39, 47) fill: Color.TRANSPARENT
    strokeWidth: 1.0
}

var outerBorderRect = Rectangle {
    x: 0 y: 0 width: stageWidth - 1 height: stageHeight - 1
    arcWidth: 8 arcHeight: 8
    stroke: Color.rgb(26, 34, 41) fill: Color.TRANSPARENT
    strokeWidth: 2.0
}

// Application User Interface
public def stage:Stage = Stage {
    title: "Shopping Service"
    resizable: false
    visible: false
    style: StageStyle.TRANSPARENT
    scene: Scene {
        content: Group {
            content: [
                bgImage, themeImage, dragBar, productList, backButton, nextButton, listButton,
                maxMinButton, closeButton, searchText,
                productInfoView, searchButton, outerBorderRect, innerBorderRect, messageView, themeBar
            ]
        }
        fill: Color.TRANSPARENT
    }
}

public function alert(msg:String): Void {
    println(msg);
    messageView.text = msg;
    messageView.show = true;
}


function run() {
    
    // Reset TextBox bounds for Mobile
    // Set background to BLACK
    if("{__PROFILE__}" == "mobile") {
        //textBounds = Rectangle { x: 0 y: 0 width: 0 height: 0 }
        stage.scene.fill = Color.BLACK;
    }
    
    // Initialize Themes
    for(i in [0..1]) {
        insert "{__DIR__}images/theme_{i}.png" into themes;
    }
    
    // Set background to black for Applets
    if("{__PROFILE__}" == "browser") {
        stage.scene.fill = Color.BLACK;
    }
    stage.visible = true;
    searchText.requestFocus();
}