package cnp.ew.grid;

import java.awt.*;

import cnp.ew.list.*;
import cnp.ew.scrolling.*;
import cnp.ew.util.*;
import cnp.ew.text.*;
import cnp.ew.misc.*;
import cnp.ew.notebook.*;
import cnp.ew.layout.*;
import cnp.ew.button.*;
import cnp.ew.displayer.*;
import cnp.ew.kdemo.*;
import cnp.ew.charts.*;

public class CpGridLc extends CpScrollingController
{
    static Font defaultHeaderFont = new Font("Helvetica", Font.BOLD, 12);
    static final Cp3DBorderDisplayer dotDisplayer = new Cp3DBorderDisplayer(true, false, true);

    CpGridPane gridPane;
    CpGridEntry  entry;
    CpPanelLc entryHolder;
    CpColumnHeader columnHeader;
    CpRowHeader rowHeader;
    CpScroller rowHeaderHolder, columnHeaderHolder;
    CpScrollingNotebookTabsLc notebookTabs;
    CpReadOnlyTextAreaLc selRangeText;
    CpToolBarButtonLc checkButton, xButton;

    boolean showHeaders = true;
    boolean showEntry = true;

    static int checkId;
    static int xId;

    static {
         checkId = CpToolkit.registerImageName("sscheck.gif");
         xId = CpToolkit.registerImageName("ssx.gif");
    }

    boolean editMode = false;

	static final int rowHeaderWidth = 30;

    public CpGridLc()
    {
        this(64, 64);
    }

    public CpGridLc(int cellsWide, int cellsTall)
    {
        this(true, true, new Dimension(cellsWide, cellsTall));
    }

    public CpGridLc(boolean hasVerticalScrollbar, boolean hasHorizontalScrollbar, Dimension sizeInCells)
	{
        super(hasVerticalScrollbar, hasHorizontalScrollbar);

        scroller.setScrollable(gridPane = new CpGridPane(sizeInCells));
        gridPane.addObserver(this);
	    gridPane.setBackground(Color.white);

        add(entryHolder = new CpPanelLc());
        CpAttachmentsLayout layout = new CpAttachmentsLayout();
        entryHolder.setLayout(layout);

        entry = new CpGridEntry();
        entry.setWantsCr(true);

        selRangeText = new CpReadOnlyTextAreaLc("A1");
	    entryHolder.add(selRangeText);
        CpAttachments attachments = new CpAttachments(2, 2, -1, -1);
        attachments.setWidth(100);
        attachments.setHeight(entry.preferredSize().height - 4);
        layout.setAttachments(selRangeText, attachments);
	    selRangeText.setBorderStyle(BORDER_LIGHTINWARD3D);
	    selRangeText.setHorizontalAlignment(CpAlignable.ALIGN_CENTER);
	    selRangeText.setVerticalAlignment(CpAlignable.ALIGN_CENTER);

        checkButton = new CpToolBarButtonLc(CpToolkit.getImage(checkId), "Accept", "Accept the current input");
        entryHolder.add(checkButton);
        attachments = new CpAttachments(-1, 2, -1, -1);
        attachments.setLeftAttachment(1, selRangeText);
        attachments.setHeight(entry.preferredSize().height - 4);
        attachments.setWidth(entry.preferredSize().height - 4);
        layout.setAttachments(checkButton, attachments);
        checkButton.addObserver(this);

        xButton = new CpToolBarButtonLc(CpToolkit.getImage(xId), "Cancel", "Cancel the current input");
        attachments = new CpAttachments(-1, 2, -1, -1);
        attachments.setLeftAttachment(1, checkButton);
        attachments.setHeight(entry.preferredSize().height - 4);
        attachments.setWidth(entry.preferredSize().height - 4);
        layout.setAttachments(xButton, attachments);
        entryHolder.add(xButton);
        xButton.addObserver(this);

	    entryHolder.add(entry);
        attachments = new CpAttachments(-1, 0, 0, -1);
        attachments.setLeftAttachment(1, xButton);
        layout.setAttachments(entry, attachments);
	    entry.setBorderStyle(BORDER_INWARD3D);
	    entry.setVerticalAlignment(CpAlignable.ALIGN_CENTER);
	    entry.setBackground(Color.white);
	    entry.addObserver(this);

		columnHeader = new CpColumnHeader(gridPane);
	    add(columnHeaderHolder = new CpScroller());
	    columnHeaderHolder.setScrollable(columnHeader);

		columnHeader.addObserver(this);
		// Issue:  Use Ken's default fonts
		columnHeader.setFont(defaultHeaderFont);
		columnHeader.setColumns(gridPane.columns[0]);

		rowHeader = new CpRowHeader(gridPane);
	    add(rowHeaderHolder = new CpScroller());
	    rowHeaderHolder.setScrollable(rowHeader);
		rowHeader.addObserver(this);
		rowHeader.setFont(defaultHeaderFont);
		rowHeader.setColumns(gridPane.rows[0]);

        notebookTabs = new CpScrollingNotebookTabsLc(new CpBeveledTabDisplayer(), CpScrollingNotebookTabsLc.ORIENT_BOTTOM, CpScrollingNotebookTabsLc.BUTTONS_LEFT);
        for (int i = 1; i < 10; i++) {
            // Wish I could pass in null as the lc...
            notebookTabs.addTab("Sheet" + i);
        }
        notebookTabs.setFont(CpFonts.dialogPlainTen());
        add(notebookTabs);
        notebookTabs.addObserver(this);

		setEditMode(false);
    }

    public Insets scrollerInsets()
    {
        Insets insets = insets();
        int entryHeight = 0;
        if (showEntry) {
            entryHeight = entry.preferredSize().height;
        }
        return new Insets(insets.top + entryHeight, insets.left, insets.bottom, insets.right);
    }

    public synchronized void layout()
    {
        super.layout();

        Insets insets = insets();
        if (showEntry) {
            entryHolder.reshape(insets.left, insets.top, size().width - insets.left - insets.right, entry.preferredSize().height);
        } else {
            entryHolder.resize(size().width - insets.left - insets.right, 0);
        }

        insets = scrollerInsets();

        int verticalScrollBarWidth, horizontalScrollBarHeight;
        if (verticalScrollBar == null) {
            verticalScrollBarWidth = 0;
        } else {
            verticalScrollBarWidth = verticalScrollBar.size().width;
        }
        if (horizontalScrollBar == null) {
            horizontalScrollBarHeight = 0;
        } else {
            horizontalScrollBarHeight = horizontalScrollBar.size().height;
        }

        columnHeaderHolder.reshape(rowHeaderWidth + insets.left, insets.top, size().width - verticalScrollBarWidth - insets.left - insets.right - rowHeaderWidth, gridPane.defaultCellExtent.height);
       	rowHeaderHolder.reshape(insets.left, insets.top + gridPane.defaultCellExtent.height, rowHeaderWidth, size().height - horizontalScrollBarHeight - insets.top - insets.left - gridPane.defaultCellExtent.height);
	    if (!showHeaders) {
	        columnHeaderHolder.resize(columnHeaderHolder.size().width, 0);
	        rowHeaderHolder.resize(0, rowHeaderHolder.size().height);
	    }
        columnHeader.reshape(0, 0, 10000, columnHeaderHolder.size().height);
        rowHeader.reshape(0, 0, rowHeaderHolder.size().width, 10000);

        if (horizontalScrollBar != null) {
            int horizontalWidth = (size().width - insets.left - insets.right - verticalScrollBar.preferredSize().width) / 2;
            horizontalScrollBar.reshape(insets.left + horizontalWidth, size().height - horizontalScrollBar.preferredSize().height - insets.bottom, horizontalWidth, horizontalScrollBar.preferredSize().height);
            notebookTabs.reshape(insets.left, horizontalScrollBar.location().y, horizontalWidth, horizontalScrollBar.size().height);
        }

        if (scroller != null) {
            Dimension scrollerSize = scroller.size();
            scroller.reshape(insets.left + rowHeaderHolder.size().width, insets.top + columnHeaderHolder.size().height, scrollerSize.width - rowHeaderHolder.size().width, scrollerSize.height - columnHeaderHolder.size().height);
        }
    }

    /************** PUBLIC API FORWARDED TO PANE **********************/

    public void toggleGrid()
    {
        gridPane.toggleGrid();
    }

    public void toggleHeaders()
    {
        enableDamageRepair(false);
        showHeaders = !showHeaders;
        layout();
        enableDamageRepair(true);
        repaint();
    }

    public void toggleEntry()
    {
        showEntry = !showEntry;
        layout();
        repaint();
    }

    public void showSelection(boolean bool)
    {
        gridPane.showSelection(bool);
    }

    public void setSelText(String s)
    {
        selRangeText.setText(s);
    }

	public void setFont(Font f)
	{
		super.setFont(f);
		gridPane.setFont(f);
		entry.setFont(f);
		columnHeader.setFont(f);
		rowHeader.setFont(f);
	}

    String getStatusText()
    {
        Point selStart = gridPane.getSelectionStart();
        Point selEnd = gridPane.getSelectionEnd();
        String selectionText = gridPane.getStringForCell(selStart);
        if (!selStart.equals(selEnd)) {
            selectionText = "(" + selectionText + ":" + gridPane.getStringForCell(selEnd) + ")";
        }
        return selectionText;
 //       return "Selection: " + selectionText + ", " + ((selEnd.x - selStart.x + 1) * (selEnd.y - selStart.y + 1)) + " cells";
    }

    /************************* EVENTS *****************************/

    public void update(CpObservable o, int facet, Object arg)
    {
        switch (facet) {
            case BUTTON_CLICKED : {
                if (o == checkButton) {
                    update(null, GRID_END_EDIT, null);
                } else if (o == xButton) {
    			    entry.setText(gridPane.getTextForEditing());
		            gridPane.setStringBeingEdited(null);
                    setEditMode(false);
                }
                break;
            }
            case COLUMN_SELECTED : {
                gridPane.rowOrColumnSelected(((Integer)arg).intValue(), o == columnHeader);
                break;
            }
            case COLUMN_SIZED : {
                gridPane.rowOrColumnSized(((Integer)arg).intValue(), o == columnHeader);
                break;
            }
            case CpTabsLc.TAB_CHANGED : {
                int i = notebookTabs.getSelectedTabIndex();
                enableDamageRepair(false);
                gridPane.selectSheet(i);
                columnHeader.setColumns(gridPane.columns[i]);
                rowHeader.setColumns(gridPane.rows[i]);
                enableDamageRepair(true);
                repaint();
                break;
            }
            case SCROLLER_PARAMETERS_CHANGED : {
                // Issue:  Seems clever.  Is this the best way to scroll in sync?
                Point scrollableLoc = scroller.getScrollableLocationInCells();
                rowHeader.scrollTo(0, scrollableLoc.y);
                columnHeader.scrollTo(scrollableLoc.x, 0);
                break;
			}

			case GRID_SELECTION_CHANGED: {
			    entry.setText(gridPane.getTextForEditing());
                setSelText(getStatusText());
			    notifyObservers(GRID_SELECTION_CHANGED, arg);
			    break;
			}
			case GRID_END_EDIT: {
			    if (editMode) {
			        setEditMode(false);
			        gridPane.requestFocus();
			        gridPane.acceptText();
			        // Whoa.  This should probably signal END_EDIT, not SEL_CHANGED
			        notifyObservers(GRID_SELECTION_CHANGED, arg);
			    }
			    break;
			}
			case CpEvent.OBJECT_CHANGING: {
			    setEditMode(true);
		        gridPane.setStringBeingEdited(entry.getText());
			    break;
			}

		}
	    super.update(o, facet, arg);
    }

    void setEditMode(boolean bool)
    {
        editMode = bool;
        checkButton.setIsDisabled(!bool);
	    xButton.setIsDisabled(!bool);
	}

	public boolean usesFocus()
	{
	    return true;
	}

	public boolean wantsLeftAndRightArrows()
	{
	    return true;
	}

	public boolean wantsTab()
	{
	    return true;
	}

	public boolean wantsCr()
	{
	    return true;
	}

    public boolean parentKeyDown(Event e, int key)
    {
        switch (key) {
        case Event.RIGHT:
        case 9: // Tab
            gridPane.moveSelectionRight();
            return true;
        case Event.LEFT:
            gridPane.moveSelectionLeft();
            return true;
        case Event.UP:
            gridPane.moveSelectionUp();
            return true;
        case Event.DOWN:
            gridPane.moveSelectionDown();
            return true;
        case 10: // Enter
            update(null, GRID_END_EDIT, null);
            gridPane.moveSelectionDown();
            return true;
        }
        return false;
    }

    public boolean keyDown(Event e, int key)
    {
        switch (key) {
        case 8: // Backspace
            setEditorKeyStroke("");
            break;
        default:
            // TBD:  FIX:  ISSUE.  Find out the right value for the beginning of non=control chars
            if (key > 30) {
                setEditorKeyStroke((new Character((char)key)).toString());
            }
            break;
        }
        return true;
    }

    void setEditorKeyStroke(String s)
    {
	    setEditMode(true);
	    entry.requestFocus();
	    entry.setText(s);
        gridPane.setStringBeingEdited(entry.getText());
	    entry.select(1, 1);
	}

    public CpGridPane getGridPane()
    {
        return gridPane;
    }

    public void newDocument(Object frame)
    {
        gridPane.newDocument(frame);
        columnHeader.setColumns(gridPane.columns[0]);
        rowHeader.setColumns(gridPane.rows[0]);
    }

    public void paint(Graphics g, Rectangle clip)
    {
        super.paint(g, clip);
        if (showHeaders) {
            dotDisplayer.paintIn(this, g, rowHeaderHolder.location().x - 1, columnHeaderHolder.location().y - 1, rowHeaderWidth + 1, columnHeaderHolder.size().height + 1);
        }
    }

    public Object getCopy()
    {// FIXXXX!!!
        CpGridLc spreadsheet = CpDemoExamples.basicCreateExampleSpreadSheet(true, true);
        new CpChartGridAdapter(spreadsheet.getGridPane(), CpDemoExamples.createExampleChartModel());
        spreadsheet.resize(302, 150);
        return spreadsheet;
    }
}

