/*******************************************************************************
 * Copyright (c) 2009, 2018 Red Hat, Inc.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph.core;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

public class SystemTapTextView extends SystemTapView {
    private StyledText viewer;

    private Display display;
    private int previousEnd;


    /**
     * Passing the focus request to the viewer's control.
     */
    @Override
    public void setFocus() {
        if (viewer != null && !viewer.isDisposed()) {
            viewer.setFocus();
        }
    }

    private void createViewer(Composite parent) {
        viewer = new StyledText(parent, SWT.READ_ONLY | SWT.MULTI
                | SWT.V_SCROLL | SWT.WRAP);

        viewer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        Font font = new Font(parent.getDisplay(), "Monospace", 11, SWT.NORMAL); //$NON-NLS-1$
        viewer.setFont(font);
        masterComposite = parent;
        display = masterComposite.getDisplay();
    }

    /**
     * Print with colour codes. Colour codes accepted in the form of ~(R,G,B)~,
     * and apply for the rest of the line or until another code is encountered
     * @param text
     */
    private void prettyPrintln(String text) {
        List<StyleRange> styles = new ArrayList<>();
        String[] txt = text.split("\\n"); //$NON-NLS-1$
        int lineOffset = 0;
        int inLineOffset;

        // txt[] contains text, with one entry for each new line
        for (String line: txt) {

            // Skip blank strings
            if (line.isEmpty()) {
                viewer.append(PluginConstants.NEW_LINE);
                continue;
            }

            // Search for colour codes, if none exist then continue
            String[] split_txt = line.split("~\\("); //$NON-NLS-1$
            if (split_txt.length == 1) {
                viewer.append(split_txt[0]);
                viewer.append(PluginConstants.NEW_LINE);
                continue;
            }

            inLineOffset = 0;
            for (String split: split_txt) {
                // Skip blank substrings
                if (split.isEmpty()) {
                    continue;
                }

                // Split for the number codes
                String[] coloursAndText = split.split("\\)~"); //$NON-NLS-1$

                // If the string is properly formatted, colours should be length
                // 2
                // If it is not properly formatted, don't colour (just print)
                if (coloursAndText.length != 2) {
                    for (String colourAndText: coloursAndText) {
                        viewer.append(colourAndText);
                        inLineOffset += colourAndText.length();
                    }
                    continue;
                }

                // The first element in the array should contain the colours
                String[] colours = coloursAndText[0].split(","); //$NON-NLS-1$
                if (colours.length < 3) {
                    continue;
                }

                // The second element in the array should contain the text
                viewer.append(coloursAndText[1]);

                // Create a colour based on the 3 integers (if there are any
                // more integers, just ignore)
                int R = Integer.valueOf(colours[0].replaceAll(" ", "")).intValue(); //$NON-NLS-1$ //$NON-NLS-2$
                int G = Integer.valueOf(colours[1].replaceAll(" ", "")).intValue(); //$NON-NLS-1$ //$NON-NLS-2$
                int B = Integer.valueOf(colours[2].replaceAll(" ", "")).intValue(); //$NON-NLS-1$ //$NON-NLS-2$

                if (R > 255) R = 255;
                if (G > 255) G = 255;
                if (B > 255) B = 255;

                if (R < 0) R = 0;
                if (G < 0) G = 0;
                if (B < 0) B = 0;

                Color newColor = new Color(display, R, G, B);

                // Find the offset of the current line
                lineOffset = viewer.getOffsetAtLine(viewer.getLineCount() - 1);

                // Create a new style that lasts no further than the length of
                // the line
                StyleRange newStyle = new StyleRange(lineOffset + inLineOffset,
                        coloursAndText[1].length(), newColor, null);
                styles.add(newStyle);

                inLineOffset += coloursAndText[1].length();
            }

            viewer.append(PluginConstants.NEW_LINE);
        }

        // Create a new style range
        StyleRange[] s = new StyleRange[styles.size()];
        styles.toArray(s);

        int cnt = viewer.getCharCount();

        // Using replaceStyleRanges with previousEnd, etc, effectively adds
        // the StyleRange to the existing set of Style Ranges (so we don't
        // waste time fudging with old style ranges that haven't changed)
        viewer.replaceStyleRanges(previousEnd, cnt - previousEnd, s);
        previousEnd = cnt;

        // Change focus and update
        viewer.setTopIndex(viewer.getLineCount() - 1);
        viewer.update();
    }

    /**
     * Default print, just dumps text into the viewer.
     * @param text
     */
    public void println(String text) {
        if (viewer != null && !viewer.isDisposed()) {
            viewer.append(text);
            viewer.setTopIndex(viewer.getLineCount() - 1);
            viewer.update();
        }
    }

    public void clearAll() {
        if (viewer != null && !viewer.isDisposed()) {
            previousEnd = 0;
            viewer.setText(""); //$NON-NLS-1$
            viewer.update();
        }
    }

    /**
     * Testing convenience method to see what was printed
     *
     * @return viewer text
     */
    public String getText() {
        return viewer.getText();
    }


    @Override
    public IStatus initializeView(Display targetDisplay, IProgressMonitor monitor) {
        previousEnd = 0;
        viewer.setText(""); //$NON-NLS-1$
        viewer.update();
        return Status.OK_STATUS;
    }

    @Override
    public void createPartControl(Composite parent) {
        createViewer(parent);

        addKillButton();
        addFileMenu();
        addHelpMenu();
        ViewFactory.addView(this);
    }

    @Override
    public void updateMethod() {
        if (getParser().getData() instanceof String) {
            String data = (String) getParser().getData();
            if (!data.isEmpty()) {
                prettyPrintln((String) getParser().getData());
            }
        }
    }

    @Override
    public void setViewID() {
        viewID = "org.eclipse.linuxtools.callgraph.core.staptextview";         //$NON-NLS-1$
    }

    @Override
    protected boolean createOpenAction() {
        return false;
    }

    @Override
    protected boolean createOpenDefaultAction() {
        return false;
    }
}
