package com.flagstone.cookbook;
/*
 * BasicButton.java
 * Cookbook
 *
 * Copyright (c) 2001-2010 Flagstone Software Ltd. All rights reserved.
 *
 * This code is distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, AND Flagstone HEREBY DISCLAIMS ALL SUCH
 * WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.DataFormatException;

import com.flagstone.transform.Background;
import com.flagstone.transform.Movie;
import com.flagstone.transform.MovieHeader;
import com.flagstone.transform.Place2;
import com.flagstone.transform.ShowFrame;
import com.flagstone.transform.action.Action;
import com.flagstone.transform.action.GetUrl;
import com.flagstone.transform.button.ButtonShape;
import com.flagstone.transform.button.ButtonState;
import com.flagstone.transform.button.DefineButton;
import com.flagstone.transform.datatype.Bounds;
import com.flagstone.transform.datatype.Color;
import com.flagstone.transform.datatype.CoordTransform;
import com.flagstone.transform.datatype.WebPalette;
import com.flagstone.transform.fillstyle.SolidFill;
import com.flagstone.transform.font.DefineFont2;
import com.flagstone.transform.linestyle.LineStyle1;
import com.flagstone.transform.shape.DefineShape2;
import com.flagstone.transform.text.DefineText2;
import com.flagstone.transform.util.font.AWTDecoder;
import com.flagstone.transform.util.font.Font;
import com.flagstone.transform.util.shape.Canvas;
import com.flagstone.transform.util.text.CharacterSet;
import com.flagstone.transform.util.text.TextTable;

/*
 * This example shows how to create a button using the DefineButton class which
 * will display a web page in a browser when clicked.
 *
 * To run this example, type the following on a command line:
 *
 *     java -cp ... com.flagstone.cookbook.BasicButton string file-out
 *
 * where:
 *
 *     string, the label that will be displayed on the button - enclose in
 *     quotes if the label contains spaces.
 *
 *     file-out is the path where the file will be written. If no output file
 *     is specified then a file named after the example will be written to the
 *     current directory.
 */
public class BasicButton {
    public static void main(String[] args) {
        try {
            String label = args[0];
            String out = args.length == 1 ? "BasicButton.swf" : args[1];
            BasicButton example = new BasicButton();
            Movie movie = new Movie();
            example.createMovie(movie, label);
            movie.encodeToFile(new File(out));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void createMovie(Movie movie, String title)
            throws IOException, DataFormatException {

        int uid = 1;
        int layer = 1;

        final String fontName = "Arial";
        final int fontStyle = java.awt.Font.PLAIN;
        final int fontSize = 24;

        // Load the AWT font.
        final AWTDecoder fontDecoder = new AWTDecoder();
        fontDecoder.read(new java.awt.Font(fontName, fontStyle, fontSize));
        final Font font = fontDecoder.getFonts().get(0);

        // Create a table of the characters displayed.
        final CharacterSet set = new CharacterSet();
        set.add(title);

        // Define the font containing only the characters displayed.
        DefineFont2 fontDef = font.defineFont(uid++, set.getCharacters());

        // Generate the text field used for the button text.
        final TextTable textGenerator = new TextTable(fontDef, fontSize * 20);
        DefineText2 label = textGenerator.defineText(uid++, title,
                WebPalette.BLACK.color());

        /*
         * Define the padding amount separating the text from the edge of the
         * button and the edge of the button to the edge of the screen.
         */
        int buttonPadding = 200;
        int screenPadding = 2000;

        int buttonWidth = label.getBounds().getWidth() + buttonPadding;
        int buttonHeight = label.getBounds().getHeight() + buttonPadding;

        /*
         * Define colours used to fill the buttons in each of its states.
         */
        Color lineColor = WebPalette.BLACK.color();
        Color shadowColor = WebPalette.GRAY.color();
        Color upColor = WebPalette.RED.color();
        Color overColor = WebPalette.ORANGE.color();

        /*
         * For each of the layers that make up the button draw a basic rectangle
         * with rounded corners.
         */
        int lineWidth = 20;
        int cornerRadius = 100;

        Canvas path = new Canvas();

        path.clear();
        path.setLineStyle(new LineStyle1(lineWidth, shadowColor));
        path.setFillStyle(new SolidFill(shadowColor));
        rect(path, 0, 0, buttonWidth, buttonHeight, cornerRadius);
        path.close();

        DefineShape2 shadow = path.defineShape(uid++);

        path.clear();
        path.setLineStyle(new LineStyle1(lineWidth, lineColor));
        path.setFillStyle(new SolidFill(upColor));
        rect(path, 0, 0, buttonWidth, buttonHeight, cornerRadius);
        path.close();

        DefineShape2 upShape = path.defineShape(uid++);

        path.clear();
        path.setLineStyle(new LineStyle1(lineWidth, lineColor));
        path.setFillStyle(new SolidFill(overColor));
        rect(path, 0, 0, buttonWidth, buttonHeight, cornerRadius);
        path.close();

        DefineShape2 overShape = path.defineShape(uid++);

        /*
         * Calculate the position of the text so it is centred in the button.
         */
        int xOffset = -label.getBounds().getWidth() / 2;
        int yOffset = (buttonHeight - label.getBounds().getHeight()) / 2;

        /*
         * The button will cast as shadow when it in the up state. Recessing the
         * button by changing the location of the shapes when the button is
         * clicked allows a very simple animation to be performed. More complex
         * animations can be created by using movie clips.
         */
        int xShadow = 60;
        int yShadow = 60;

        /*
         * Define coordinate transform applied to the basic button shape to
         * perform a simple animation giving the impression the button was
         * physically clicked.
         */
        CoordTransform recess = CoordTransform.translate(xShadow, yShadow);

        /*
         * Add the button records that define the button's appearance
         */
        List<ButtonShape> records = new ArrayList<ButtonShape>();

        records.add(new ButtonShape().addState(ButtonState.ACTIVE)
                .setIdentifier(upShape.getIdentifier()).setLayer(layer++));

        records.add(new ButtonShape().addState(ButtonState.UP)
                .setIdentifier(shadow.getIdentifier()).setLayer(layer++)
                .setTransform(recess));

        records.add(new ButtonShape().addState(ButtonState.UP)
                .setIdentifier(upShape.getIdentifier()).setLayer(layer++));

        records.add(new ButtonShape().addState(ButtonState.UP)
                .setIdentifier(label.getIdentifier()).setLayer(layer++)
                .setTransform(CoordTransform.translate(xOffset, yOffset)));

        records.add(new ButtonShape().addState(ButtonState.OVER)
                .setIdentifier(shadow.getIdentifier()).setLayer(layer++)
                .setTransform(recess));

        records.add(new ButtonShape().addState(ButtonState.OVER)
                .setIdentifier(overShape.getIdentifier()).setLayer(layer++));

        records.add(new ButtonShape().addState(ButtonState.OVER)
                .setIdentifier(label.getIdentifier()).setLayer(layer++)
                .setTransform(CoordTransform.translate(xOffset, yOffset)));

        records.add(new ButtonShape().addState(ButtonState.DOWN)
                .setIdentifier(overShape.getIdentifier()).setLayer(layer++)
                .setTransform(recess));

        records.add(new ButtonShape().addState(ButtonState.DOWN)
                .setIdentifier(label.getIdentifier()).setLayer(layer++)
                .setTransform(CoordTransform.translate(
                        xOffset + xShadow, yOffset + yShadow)));

        /*
         * DefineButton executes a single set of actions when it is clicked.
         * More complex behaviour can be implemented using DefineButton2 which
         * allows different sets of actions to executed in response to a wide
         * range of events.
         */
        List<Action> actions = new ArrayList<Action>();
        actions.add(new GetUrl("http://www.flagstonesoftware.com", ""));

        DefineButton button = new DefineButton(uid++, records, actions);

        /***************************************************
         * Put all the objects together in a movie
         ***************************************************/

        int screenWidth = buttonWidth + screenPadding;
        int screenHeight = buttonHeight + screenPadding;

        MovieHeader header = new MovieHeader();
        header.setFrameSize(new Bounds(0, 0, screenWidth, screenHeight));
        header.setFrameRate(1.0f);
        movie.add(header);

        movie.add(new Background(WebPalette.LIGHT_BLUE.color()));

        movie.add(fontDef);
        movie.add(shadow);
        movie.add(upShape);
        movie.add(overShape);
        movie.add(label);
        movie.add(button);

        movie.add(Place2.show(button.getIdentifier(), layer++, screenWidth / 2,
                screenHeight / 2));

        movie.add(ShowFrame.getInstance());
    }

    private void rect(Canvas path, int x, int y, int width, int height,
            int radius) {
        int shortestSide = (height < width) ? height : width;

        if (radius > shortestSide / 2)
            radius = shortestSide / 2;

        path.move(x, y - height / 2);
        path.rline(width / 2 - radius, 0);
        path.rcurve(radius, 0, 0, radius);
        path.rline(0, height - 2 * radius);
        path.rcurve(0, radius, -radius, 0);
        path.rline(-(width - 2 * radius), 0);
        path.rcurve(-radius, 0, 0, -radius);
        path.rline(0, -(height - 2 * radius));
        path.rcurve(0, -radius, radius, 0);
    }
}