/*
 * Copyright (C) 2007 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.android.ddms;

import com.android.ddmuilib.TableHelper;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Dialog to configure the static debug ports.
 *
 */
public class StaticPortConfigDialog extends Dialog {

    /** Preference name for the 0th column width */
    private static final String PREFS_DEVICE_COL = "spcd.deviceColumn"; //$NON-NLS-1$

    /** Preference name for the 1st column width */
    private static final String PREFS_APP_COL = "spcd.AppColumn"; //$NON-NLS-1$

    /** Preference name for the 2nd column width */
    private static final String PREFS_PORT_COL = "spcd.PortColumn"; //$NON-NLS-1$

    private static final int COL_DEVICE = 0;
    private static final int COL_APPLICATION = 1;
    private static final int COL_PORT = 2;


    private static final int DLG_WIDTH = 500;
    private static final int DLG_HEIGHT = 300;

    private Shell mShell;
    private Shell mParent;

    private Table mPortTable;

    /**
     * Array containing the list of already used static port to avoid
     * duplication.
     */
    private ArrayList<Integer> mPorts = new ArrayList<Integer>();

    /**
     * Basic constructor.
     * @param parent
     */
    public StaticPortConfigDialog(Shell parent) {
        super(parent, SWT.DIALOG_TRIM | SWT.BORDER | SWT.APPLICATION_MODAL);
    }

    /**
     * Open and display the dialog. This method returns only when the
     * user closes the dialog somehow.
     *
     */
    public void open() {
        createUI();

        if (mParent == null || mShell == null) {
            return;
        }

        updateFromStore();

        // Set the dialog size.
        mShell.setMinimumSize(DLG_WIDTH, DLG_HEIGHT);
        Rectangle r = mParent.getBounds();
        // get the center new top left.
        int cx = r.x + r.width/2;
        int x = cx - DLG_WIDTH / 2;
        int cy = r.y + r.height/2;
        int y = cy - DLG_HEIGHT / 2;
        mShell.setBounds(x, y, DLG_WIDTH, DLG_HEIGHT);

        mShell.pack();

        // actually open the dialog
        mShell.open();

        // event loop until the dialog is closed.
        Display display = mParent.getDisplay();
        while (!mShell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
    }

    /**
     * Creates the dialog ui.
     */
    private void createUI() {
        mParent = getParent();
        mShell = new Shell(mParent, getStyle());
        mShell.setText("Static Port Configuration");

        mShell.setLayout(new GridLayout(1, true));

        mShell.addListener(SWT.Close, new Listener() {
            @Override
            public void handleEvent(Event event) {
                event.doit = true;
            }
        });

        // center part with the list on the left and the buttons
        // on the right.
        Composite main = new Composite(mShell, SWT.NONE);
        main.setLayoutData(new GridData(GridData.FILL_BOTH));
        main.setLayout(new GridLayout(2, false));

        // left part: list view
        mPortTable = new Table(main, SWT.SINGLE | SWT.FULL_SELECTION);
        mPortTable.setLayoutData(new GridData(GridData.FILL_BOTH));
        mPortTable.setHeaderVisible(true);
        mPortTable.setLinesVisible(true);

        TableHelper.createTableColumn(mPortTable, "Device Serial Number",
                SWT.LEFT, "emulator-5554", //$NON-NLS-1$
                PREFS_DEVICE_COL, PrefsDialog.getStore());

        TableHelper.createTableColumn(mPortTable, "Application Package",
                SWT.LEFT, "com.android.samples.phone", //$NON-NLS-1$
                PREFS_APP_COL, PrefsDialog.getStore());

        TableHelper.createTableColumn(mPortTable, "Debug Port",
                SWT.RIGHT, "Debug Port", //$NON-NLS-1$
                PREFS_PORT_COL, PrefsDialog.getStore());

        // right part: buttons
        Composite buttons = new Composite(main, SWT.NONE);
        buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
        buttons.setLayout(new GridLayout(1, true));

        Button newButton = new Button(buttons, SWT.NONE);
        newButton.setText("New...");
        newButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                StaticPortEditDialog dlg = new StaticPortEditDialog(mShell,
                        mPorts);
                if (dlg.open()) {
                    // get the text
                    String device = dlg.getDeviceSN();
                    String app = dlg.getAppName();
                    int port = dlg.getPortNumber();

                    // add it to the list
                    addEntry(device, app, port);
                }
            }
        });

        final Button editButton = new Button(buttons, SWT.NONE);
        editButton.setText("Edit...");
        editButton.setEnabled(false);
        editButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                int index = mPortTable.getSelectionIndex();
                String oldDeviceName = getDeviceName(index);
                String oldAppName = getAppName(index);
                String oldPortNumber = getPortNumber(index);
                StaticPortEditDialog dlg = new StaticPortEditDialog(mShell,
                        mPorts, oldDeviceName, oldAppName, oldPortNumber);
                if (dlg.open()) {
                    // get the text
                    String deviceName = dlg.getDeviceSN();
                    String app = dlg.getAppName();
                    int port = dlg.getPortNumber();

                    // add it to the list
                    replaceEntry(index, deviceName, app, port);
                }
            }
        });

        final Button deleteButton = new Button(buttons, SWT.NONE);
        deleteButton.setText("Delete");
        deleteButton.setEnabled(false);
        deleteButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                int index = mPortTable.getSelectionIndex();
                removeEntry(index);
            }
        });

        // bottom part with the ok/cancel
        Composite bottomComp = new Composite(mShell, SWT.NONE);
        bottomComp.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_CENTER));
        bottomComp.setLayout(new GridLayout(2, true));

        Button okButton = new Button(bottomComp, SWT.NONE);
        okButton.setText("OK");
        okButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                updateStore();
                mShell.close();
            }
        });

        Button cancelButton = new Button(bottomComp, SWT.NONE);
        cancelButton.setText("Cancel");
        cancelButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                mShell.close();
            }
        });

        mPortTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // get the selection index
                int index = mPortTable.getSelectionIndex();

                boolean enabled = index != -1;
                editButton.setEnabled(enabled);
                deleteButton.setEnabled(enabled);
            }
        });

        mShell.pack();

    }

    /**
     * Add a new entry in the list.
     * @param deviceName the serial number of the device
     * @param appName java package for the application
     * @param portNumber port number
     */
    private void addEntry(String deviceName, String appName, int portNumber) {
        // create a new item for the table
        TableItem item = new TableItem(mPortTable, SWT.NONE);

        item.setText(COL_DEVICE, deviceName);
        item.setText(COL_APPLICATION, appName);
        item.setText(COL_PORT, Integer.toString(portNumber));

        // add the port to the list of port number used.
        mPorts.add(portNumber);
    }

    /**
     * Remove an entry from the list.
     * @param index The index of the entry to be removed
     */
    private void removeEntry(int index) {
        // remove from the ui
        mPortTable.remove(index);

        // and from the port list.
        mPorts.remove(index);
    }

    /**
     * Replace an entry in the list with new values.
     * @param index The index of the item to be replaced
     * @param deviceName the serial number of the device
     * @param appName The new java package for the application
     * @param portNumber The new port number.
     */
    private void replaceEntry(int index, String deviceName, String appName, int portNumber) {
        // get the table item by index
        TableItem item = mPortTable.getItem(index);

        // set its new value
        item.setText(COL_DEVICE, deviceName);
        item.setText(COL_APPLICATION, appName);
        item.setText(COL_PORT, Integer.toString(portNumber));

        // and replace the port number in the port list.
        mPorts.set(index, portNumber);
    }


    /**
     * Returns the device name for a specific index
     * @param index The index
     * @return the java package name of the application
     */
    private String getDeviceName(int index) {
        TableItem item = mPortTable.getItem(index);
        return item.getText(COL_DEVICE);
    }

    /**
     * Returns the application name for a specific index
     * @param index The index
     * @return the java package name of the application
     */
    private String getAppName(int index) {
        TableItem item = mPortTable.getItem(index);
        return item.getText(COL_APPLICATION);
    }

    /**
     * Returns the port number for a specific index
     * @param index The index
     * @return the port number
     */
    private String getPortNumber(int index) {
        TableItem item = mPortTable.getItem(index);
        return item.getText(COL_PORT);
    }

    /**
     * Updates the ui from the value in the preference store.
     */
    private void updateFromStore() {
        // get the map from the debug port manager
        DebugPortProvider provider = DebugPortProvider.getInstance();
        Map<String, Map<String, Integer>> map = provider.getPortList();

        // we're going to loop on the keys and fill the table.
        Set<String> deviceKeys = map.keySet();

        for (String deviceKey : deviceKeys) {
            Map<String, Integer> deviceMap = map.get(deviceKey);
            if (deviceMap != null) {
                Set<String> appKeys = deviceMap.keySet();

                for (String appKey : appKeys) {
                    Integer port = deviceMap.get(appKey);
                    if (port != null) {
                        addEntry(deviceKey, appKey, port);
                    }
                }
            }
        }
    }

    /**
     * Update the store from the content of the ui.
     */
    private void updateStore() {
        // create a new Map object and fill it.
        HashMap<String, Map<String, Integer>> map = new HashMap<String, Map<String, Integer>>();

        int count = mPortTable.getItemCount();

        for (int i = 0 ; i < count ; i++) {
            TableItem item = mPortTable.getItem(i);
            String deviceName = item.getText(COL_DEVICE);

            Map<String, Integer> deviceMap = map.get(deviceName);
            if (deviceMap == null) {
                deviceMap = new HashMap<String, Integer>();
                map.put(deviceName, deviceMap);
            }

            deviceMap.put(item.getText(COL_APPLICATION), Integer.valueOf(item.getText(COL_PORT)));
        }

        // set it in the store through the debug port manager.
        DebugPortProvider provider = DebugPortProvider.getInstance();
        provider.setPortList(map);
    }
}
