public final class

PathBarLayer

extends Layer
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cooliris.media;

import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL11;

import android.graphics.Typeface;
import android.view.MotionEvent;

import com.cooliris.app.App;
import com.cooliris.app.Res;

public final class PathBarLayer extends Layer {
    private static final StringTexture.Config sPathFormat = new StringTexture.Config();
    private final ArrayList<Component> mComponents = new ArrayList<Component>();
    private static final int FILL = Res.drawable.pathbar_bg;
    private static final int JOIN = Res.drawable.pathbar_join;
    private static final int CAP = Res.drawable.pathbar_cap;
    private Component mTouchItem = null;

    static {
        sPathFormat.fontSize = 18f * App.PIXEL_DENSITY;
    }

    public PathBarLayer() {
    }

    public void pushLabel(int icon, String label, Runnable action) {
        synchronized (mComponents) {
            mComponents.add(new Component(icon, label, action, 0));
        }
        recomputeComponents();
    }

    public void setAnimatedIcons(final int[] icons) {
        synchronized (mComponents) {
            final int numComponents = mComponents.size();
            for (int i = 0; i < numComponents; ++i) {
                final Component component = mComponents.get(i);
                if (component != null) {
                    if (component.animatedIcons != null) {
                        component.animatedIcons = null;
                    }
                    if (i == numComponents - 1) {
                        component.animatedIcons = icons;
                    }
                }
            }
        }
    }

    public void changeLabel(String label) {
        if (label == null || label.length() == 0)
            return;
        Component component = popLabel();
        if (component != null) {
            pushLabel(component.icon, label, component.action);
        }
    }

    public String getCurrentLabel() {
        final ArrayList<Component> components = mComponents;
        synchronized (components) {
            int lastIndex = components.size() - 1;
            if (lastIndex < 0) {
                return "";
            }
            Component retVal = components.get(lastIndex);
            return retVal.origString;
        }
    }

    public Component popLabel() {
        final ArrayList<Component> components = mComponents;
        synchronized (components) {
            int lastIndex = components.size() - 1;
            if (lastIndex < 0) {
                return null;
            }
            Component retVal = components.get(lastIndex);
            components.remove(lastIndex);
            return retVal;
        }
    }

    private static final class Component {
        public String origString;
        public int icon;
        public Runnable action;
        public StringTexture texture;
        public float width;
        public float animWidth;
        public float x;
        public int[] animatedIcons;
        public float timeElapsed;
        private static final float ICON_WIDTH = 38.0f;

        Component(int icon, String label, Runnable action, float widthLeft) {
            this.action = action;
            origString = label;
            this.icon = icon;
            computeLabel(widthLeft);
        }

        public final void computeLabel(float widthLeft) {
            Typeface typeface = sPathFormat.bold ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
            String label = "";
            if (origString != null) {
                label = origString.substring(0, StringTexture.lengthToFit(sPathFormat.fontSize, widthLeft, typeface, origString));
                if (label.length() != origString.length()) {
                    label += "...";
                }
            }
            this.texture = new StringTexture(label, sPathFormat);
        }

        public final boolean update(float timeElapsed) {
            this.timeElapsed += timeElapsed;
            if (animWidth == 0.0f) {
                animWidth = width;
            }
            animWidth = FloatUtils.animate(animWidth, width, timeElapsed);
            if (animatedIcons != null && animatedIcons.length > 1)
                return true;
            if (animWidth == width) {
                return false;
            } else {
                return true;
            }
        }

        public float getIconWidth() {
            return ICON_WIDTH * App.PIXEL_DENSITY;
        }
    }

    @Override
    public void generate(RenderView view, RenderView.Lists lists) {
        lists.blendedList.add(this);
        lists.hitTestList.add(this);
        lists.updateList.add(this);
    }

    private void layout() {
        synchronized (mComponents) {
            int numComponents = mComponents.size();
            for (int i = 0; i < numComponents; ++i) {
                Component component = mComponents.get(i);
                if (component == null)
                    continue;
                float iconWidth = (component.icon == 0) ? 0 : component.getIconWidth();
                if (iconWidth == 0) {
                    iconWidth = 8 * App.PIXEL_DENSITY;
                }
                float offset = 5 * App.PIXEL_DENSITY;
                float thisComponentWidth = (i != numComponents - 1) ? iconWidth + offset : component.texture.computeTextWidth()
                        + iconWidth + offset;
                component.width = thisComponentWidth;
            }
        }
    }

    @Override
    public boolean update(RenderView view, float timeElapsed) {
        layout();
        boolean retVal = false;
        synchronized (mComponents) {
            int numComponents = mComponents.size();
            for (int i = 0; i < numComponents; i++) {
                Component component = mComponents.get(i);
                retVal |= component.update(timeElapsed);
            }
        }
        return retVal;
    }

    @Override
    public void renderBlended(RenderView view, GL11 gl) {
        // Draw components.
        final Texture fill = view.getResource(FILL);
        final Texture join = view.getResource(JOIN);
        final Texture cap = view.getResource(CAP);
        final float y = mY + 3;
        int x = (int) (3 * App.PIXEL_DENSITY);
        float height = mHeight;
        synchronized (mComponents) {
            int numComponents = mComponents.size();
            for (int i = 0; i < numComponents; ++i) {
                Component component = mComponents.get(i);
                component.x = x;
                // Draw the left join if not the first component, and the fill.
                // TODO: Draw the pressed background for mTouchItem.
                final int width = (int) component.animWidth;
                if (i != 0) {
                    view.draw2D(join, x - join.getWidth(), y);
                    if (view.bind(fill)) {
                        view.draw2D(x, y, 0f, width, height);
                    }
                } else if (view.bind(fill)) {
                    view.draw2D(0f, y, 0f, x + width, height);
                }

                if (i == numComponents - 1) {
                    // Draw the cap on the right edge.
                    view.draw2D(cap, x + width, y);
                }
                float xOffset = 5 * App.PIXEL_DENSITY;
                // Draw the label.
                final int[] icons = component.animatedIcons;

                // Cycles animated icons.
                final int iconId = (icons != null && icons.length > 0) ? icons[(int) (component.timeElapsed * 20.0f) % icons.length]
                        : component.icon;
                final Texture icon = view.getResource(iconId);
                if (icon != null) {
                    view.loadTexture(icon);
                    view.draw2D(icon, x + xOffset, y - 2 * App.PIXEL_DENSITY);
                }
                if (i == numComponents - 1) {
                    final StringTexture texture = component.texture;
                    view.loadTexture(texture);
                    float iconWidth = component.getIconWidth();
                    if (texture.computeTextWidth() <= (width - iconWidth)) {
                        float textOffset = (iconWidth == 0) ? 8 * App.PIXEL_DENSITY : iconWidth;
                        view.draw2D(texture, x + textOffset, y + 5);
                    }
                }
                x += (int) (width + (21 * App.PIXEL_DENSITY + 0.5f));
            }
        }
    }

    private Component hitTestItems(float x, float y) {
        if (y >= mY && y < mY + mHeight) {
            synchronized (mComponents) {
                int numComponents = mComponents.size();
                for (int i = 0; i < numComponents; i++) {
                    final Component component = mComponents.get(i);
                    float componentx = component.x;
                    if (x >= componentx && x < componentx + component.width) {
                        return component;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mTouchItem = hitTestItems(x, y);
            break;
        case MotionEvent.ACTION_MOVE:
            break;
        case MotionEvent.ACTION_UP:
            if (mTouchItem != null) {
                mTouchItem.action.run();
            }
        case MotionEvent.ACTION_CANCEL:
            mTouchItem = null;
            break;
        }
        return true;
    }

    public void recomputeComponents() {
        float width = mWidth;
        width -= 20f * App.PIXEL_DENSITY;
        synchronized (mComponents) {
            int numComponents = mComponents.size();
            for (int i = 0; i < numComponents; i++) {
                Component component = mComponents.get(i);
                width -= (component.getIconWidth() + 20.0f * App.PIXEL_DENSITY);
                component.computeLabel(width);
            }
        }
    }

    public int getNumLevels() {
        synchronized (mComponents) {
            return mComponents.size();
        }
    }

    public void clear() {
        synchronized (mComponents) {
            mComponents.clear();
        }
    }
}