/* -*- Mode: java; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */

package com.danger.terminal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import danger.app.Event;
import danger.ui.Bitmap;
import danger.ui.Menu;
import danger.ui.Pen;
import danger.ui.Rect;
import danger.ui.Style;
import danger.ui.ActiveListView;
import danger.util.StdActiveList;


public class SessionListView extends ActiveListView
    implements TerminalResources, TerminalEvents, SessionManager.SessionListener
{
    public void
    init (SessionManager manager)
    {
        mSessionManager = manager;
        mSessionManager.registerListener(this);
        mMoveMode = false;
        mUpTriangle = Terminal.instance().getBitmap(ID_UP_TRIANGLE);
        mDownTriangle = Terminal.instance().getBitmap(ID_DOWN_TRIANGLE);
    }

    
    // ------------------------------------------------------------------------
    // public instance methods

    // superclass's javadoc suffices
    public void
    onAddedToParent ()
    {
        mWindow = (SessionListWindow) getWindow();
        
        mSessionList = new StdActiveList();
        setItemHeight(15);
        setList(mSessionList);
    }


    // ------------------------------------------------------------------------
    // protected instance methods

    // superclass's javadoc suffices
    protected void
    itemFocused (Object item)
    {
        if (item == null) {
            mWindow.deactivateMenu();
        } else if (mMoveMode) {
            mWindow.useMoveMenu();
        } else if (item.getClass() == SessionListItem.class) {
            SessionListItem slitem = (SessionListItem) item;
            
            if (slitem.getType() == SessionListItem.SERVER_TYPE) {
                mWindow.useServerMenu();
                if (slitem.getServerInfo().getConsoles().size() == 0) {
                    mWindow.enableDelete(true);
                } else {
                    mWindow.enableDelete(false);
                }
            } else {
                // SESSION_TYPE
                mWindow.useSessionMenu();
            }
        }
    }

    // superclass's javadoc suffices
    protected void
    itemActivated (Object item)
    {
        if (item != null) {
            if (mMoveMode) {
                leaveMoveMode(true);
                return;
            }
            ((SessionListItem) item).itemActivated();
        }
    }

    // superclass's javadoc suffices
    protected void
    itemUpdated (Object item, boolean isFocused)
    {
        if (isFocused) {
            itemFocused(item);
        }
    }

    public void
    serverListChanged ()
    {
        updateSessionList();
    }
    
    public void
    serverAdded (ServerInfo info)
    {
        addServer(info);
    }
    
    public void
    serverRemoved (ServerInfo info)
    {
        removeServer(info);
    }
    
    public void
    serverUpdated (ServerInfo info)
    {
        invalidate();
    }
    
    public void
    sessionUpdated (TerminalConsole console)
    {
        invalidate();
    }

    public boolean
    eventWidgetDown (int widget, Event event)
    {
        if (mMoveMode) {
            switch (widget) {
            case Event.DEVICE_WHEEL_PAGE_DOWN:
            case Event.DEVICE_ARROW_DOWN:
            case Event.DEVICE_WHEEL:
            case Event.DEVICE_MULTIPLE_WHEEL:
                // for now, it's easiest just to move one hop at a time
                moveDown();
                return true;
                
            case Event.DEVICE_ARROW_UP:
                moveUp();
                return true;
                
            case Event.DEVICE_BUTTON_MENU:
                // swallow it
                System.err.println("swallow down!");
                return true;
                
            case Event.DEVICE_NAVIGATION:
                if ((event.modifiers & Event.EVENT_MODIFIER_TRACKBALL) != 0) {
                    // only handle up/down trackball events, translate to scrolling
                    byte y = event.getVerticalCount();
                    if (y > 0) {
                        for (int i = 0; i < y; i++) {
                            moveDown();
                        }
                    } else if (y < 0) {
                        for (int i = 0; i < -y; i++) {
                            moveUp();
                        }
                    }
                    return true;
                }
                break;
            }
        }

        return super.eventWidgetDown(widget, event);
    }
    
    public boolean
    eventWidgetUp (int widget, Event event)
    {
        if (mMoveMode) {
            switch (widget) {
            case Event.DEVICE_WHEEL_PAGE_UP:
            case Event.DEVICE_WHEEL:
            case Event.DEVICE_MULTIPLE_WHEEL:
                moveUp();
                return true;
            case Event.DEVICE_BUTTON_BACK:
            case Event.DEVICE_BUTTON_CANCEL:
                leaveMoveMode(false);
                return true;
            case Event.DEVICE_BUTTON_MENU:
                // swallow it
                System.err.println("swallow up!");
                return true;
            }
        }

        return super.eventWidgetUp(widget, event);
    }
    
    public final boolean
    eventWidgetHeld (int widget, Event event)
    {
        if (! mMoveMode && (widget == Event.DEVICE_WHEEL_BUTTON)) {
            showContextMenu();
            return true;
        }

        return super.eventWidgetHeld(widget, event);
    }

    private void
    showContextMenu ()
    {
        Menu menu = null;
        SessionListItem item = (SessionListItem) getFocusedItem();
        if (item.getServerInfo() == null) {
            // session
            menu = Terminal.instance().getResources().getMenu(ID_SESSION_CONTEXT_MENU);
        } else {
            // server
            menu = Terminal.instance().getResources().getMenu(ID_SERVER_CONTEXT_MENU);
        }

        menu.setWindow(getWindow());
        Rect r = new Rect();
        getInterestingRect(r);
        menu.setPosition(70, (r.top + r.bottom) / 2);
        menu.setListenerForAllItems(mWindow);
        menu.enterMenu(true);
    }
    
    private void
    paintSelection (Pen p, int left, int top, int right, int bottom, Object item)
    {
        if (! mMoveMode) {
            p.setColor(getStyleColor(Style.SELECTION_BACKGROUND_COLOR));
            p.fillRoundRect(left, top, right, bottom, CORNER_SIZE, CORNER_SIZE);
            p.setColor(getStyleColor(Style.BACKGROUND_COLOR));
            return;
        }

        int index = mSessionList.eqIndexOf(item);
        boolean atTop = (index == 0);
        boolean atBottom = (index == mSessionList.size() - 1);

        p.setColor(getStyleColor(Style.SELECTION_BACKGROUND_COLOR));
//        p.setDashed(true);
        p.drawRoundRect(left, top, right, bottom, CORNER_SIZE, CORNER_SIZE);
//        p.setDashed(false);

        if (! atTop) {
            p.drawBitmap(right - 11, top - 3, mUpTriangle);
        }
        if (! atBottom) {
            p.drawBitmap(right - 11, bottom + 0, mDownTriangle);
        }
    }
    
    protected void
    paintItemLine (Pen p, int left, int top, int right, int bottom, boolean focused, Object item)
    {        
        /* this is just stolen from super */
        if (focused) {
            paintSelection(p, left, top, right, bottom, item);
        } else {
            p.setColor(getStyleColor(Style.FOREGROUND_COLOR));
        }

        ((SessionListItem) item).paintItem(getStyle(), p, left, top, right, bottom, focused);
    }

    protected SessionListItem
    addServer (ServerInfo info)
    {
        SessionListItem item = new SessionListItem(info);
        if (mMoveMode) {
            mMoveList.addItem(item);
        } else {
            mSessionList.addItem(item);
        }
        return item;
    }

    protected void
    removeServer (ServerInfo info)
    {
        /* find the session list item */
        int index = findServerIndex(info);
        if (index >= 0) {
            mSessionList.removeItemAt(index);

            /* now remove the consoles */
            for (int i = info.getConsoles().size() - 1; i >= 0; --i) {
                mSessionList.removeItemAt(index);
            }

            if (mSessionList.size() == 0) {
                mWindow.receiveEvent(new Event(EVENT_NEW_SERVER, 0, 0, null));
            }
        }
    }

    private int
    findServerIndex (ServerInfo info)
    {
        for (int index = mSessionList.size() - 1; index >= 0; --index) {
            SessionListItem item = (SessionListItem) mSessionList.getItem(index);
            if (item.getServerInfo() == info) {
                return index;
            }
        }

        return -1;
    }

    private int
    findConsoleIndex (TerminalConsole console)
    {
        for (int index = mSessionList.size() - 1; index >= 0; --index) {
            SessionListItem item = (SessionListItem) mSessionList.getItem(index);
            if (item.getConsole() == console) {
                return index;
            }
        }

        return -1;
    }

    public void
    sessionAdded (TerminalConsole console)
    {
        int index = findServerIndex(console.getHostInfo());

        if (index >= 0) {
            // scan past existing sessions
            for (index++; index < mSessionList.size(); index++) {
                SessionListItem item = (SessionListItem) mSessionList.getItem(index);
                if (item.getConsole() == null) {
                    break;
                }
            }

            SessionListItem item = new SessionListItem(console);
            mSessionList.insertItemAt(item, index);
            invalidate();
        }
    }

    public void
    sessionRemoved (TerminalConsole console)
    {
        int index = findConsoleIndex(console);
        if (index >= 0) {
            mSessionList.removeItemAt(index);
        }
    }

    // re-populate the session list view, because some saved servers changed.
    private void
    updateSessionList ()
    {
        if (mSessionList == null) {
            return;
        }
        
        // save all the existing open sessions
        List openSessions = new ArrayList();
        int count = mSessionList.size();
        for (int i = 0; i < count; i++) {
            SessionListItem item = (SessionListItem) mSessionList.getItem(i);
            if (item.getConsole() != null) {
                openSessions.add(item);
            }
        }
        
        /* remove everything and then add them all back */
        mSessionList.removeAllItems();
        
        List hostList = mSessionManager.getServerList();
        for (Iterator i = hostList.iterator(); i.hasNext(); ) {
            ServerInfo server = (ServerInfo) i.next();
            addServer(server);
            
            // add all open consoles for this host
            for (Iterator j = openSessions.iterator(); j.hasNext(); ) {
                SessionListItem item = (SessionListItem) j.next();
                TerminalConsole console = item.getConsole();
                if ((console != null) && (console.getHostInfo() == server)) {
                    mSessionList.insertItemAt(item, mSessionList.size());
                    j.remove();
                }
            }
        }
        
        if (hostList.isEmpty()) {
            mWindow.receiveEvent(new Event(EVENT_NEW_SERVER, 0, 0, null));
        }
    }
    
    private void
    moveUp ()
    {
        SessionListItem item = (SessionListItem) getFocusedItem();
        int index = mMoveList.eqIndexOf(item);
        if (index == 0) {
            return;
        }
        
        mMoveList.removeItem(item);
        mMoveList.insertItemAt(item, index - 1);
        setFocusedItem(item);
    }
    
    private void
    moveDown ()
    {
        SessionListItem item = (SessionListItem) getFocusedItem();
        int index = mMoveList.eqIndexOf(item);
        if (index == mMoveList.size() - 1) {
            return;
        }
        
        mMoveList.removeItem(item);
        mMoveList.insertItemAt(item, index + 1);
        setFocusedItem(item);
    }
    
    public void
    enterMoveMode ()
    {
        List hostList = mSessionManager.getServerList();
        if (hostList.isEmpty()) {
            // you can't enter move mode.
            return;
        }
        
        mMoveMode = true;
        mMoveItem = ((SessionListItem) getFocusedItem()).getServerInfo();
        
        mMoveList = new StdActiveList();
        setList(mMoveList);
        for (Iterator i = hostList.iterator(); i.hasNext(); ) {
            ServerInfo server = (ServerInfo) i.next();
            SessionListItem item = addServer(server);
            if (server.compareTo(mMoveItem) == 0) {
                setFocusedItem(item);
            }
        }

        invalidate();
    }
    
    public void
    leaveMoveMode (boolean commit)
    {
        mMoveMode = false;
        
        if (commit) {
            int oldIndex = mMoveItem.getSettingsIndex();
            int newIndex = mMoveList.eqIndexOf(getFocusedItem());
            int direction = (oldIndex < newIndex) ? 1 : -1;
            for (int i = oldIndex; i != newIndex; i += direction) {
                ServerInfo info = ((SessionListItem) mMoveList.getItem(i)).getServerInfo();
                info.setSettingsIndex(i);
            }
            ((SessionListItem) getFocusedItem()).getServerInfo().setSettingsIndex(newIndex);

            mSessionManager.reorderServers();
            mSessionManager.saveSessionPrefs();
            updateSessionList();
        }
        
        setList(mSessionList);
                
        int count = mSessionList.size();
        for (int i = 0; i < count; i++) {
            SessionListItem item = (SessionListItem) mSessionList.getItem(i);
            ServerInfo server = item.getServerInfo();
            if ((server != null) && (server.compareTo(mMoveItem) == 0)) {
                setFocusedItem(item);
            }
        }
        
        mMoveList = null;
        mMoveItem = null;
        invalidate();
    }
    
    public boolean
    inMoveMode ()
    {
        return mMoveMode;
    }
    
    
    static private final int            CORNER_SIZE = 3;

    private SessionListWindow           mWindow;
    private SessionManager              mSessionManager;
    private StdActiveList               mSessionList;
    private StdActiveList               mMoveList;
    private Bitmap                      mUpTriangle;
    private Bitmap                      mDownTriangle;

    private boolean                     mMoveMode;
    private ServerInfo                  mMoveItem;
}
