
package cz.karry.dop.nbmodule.dbg;

import cz.karry.dop.Logger;
import cz.karry.dop.nbmodule.dbg.ui.DopDbgOutTopComponent;
import cz.karry.dop.nbmodule.dbg.ui.DopDbgTopComponent;
import cz.karry.dop.nbmodule.dbg.ui.DopDbgMemViewTopComponent;
import org.openide.windows.TopComponent;
import cz.karry.dop.core.Core;
import cz.karry.dop.memory.Memory;
import cz.karry.dop.nbmodule.files.sidebar.SideBar;
import cz.karry.dop.parser.MemorySizeError;
import cz.karry.dop.parser.Parser;
import cz.karry.dop.parser.SyntaxError;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import javax.swing.JEditorPane;
import javax.swing.text.Caret;
import javax.swing.text.StyledDocument;
import org.netbeans.editor.AnnotationDesc;
import org.netbeans.editor.AnnotationTypes;
import org.netbeans.editor.BaseDocument;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.MultiDataObject;
import org.openide.nodes.Node;
import org.openide.text.NbDocument;


/**
 *
 * @author karry
 */
public class Session implements cz.karry.dop.Logger{
    
    private static Session sessionInstance = null;
    //private FileObject fileObject;
    private String source = "";
    private Core dopCore = null;
    private File file;
    private File memFile;
    private String memoryData;
    private Memory memory;
    private DbgAnnotation currentInstrAnnotacion;


    public Session(File f, File memFile, Node node) {
        //         String displayName = FileUtil.getFileDisplayName(f);
        sessionInstance = this;

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                TopComponent win;
                        
                win = DopDbgOutTopComponent.findInstance();
                win.open();
                win.requestActive();
                
                win = DopDbgMemViewTopComponent.findInstance();
                win.open();
                win.requestActive();                

                win = DopDbgTopComponent.findInstance();
                win.open();
                win.requestActive();
            }
        });
        

        init(f, memFile, node);
    }

    public static Session getInstance() {
        return sessionInstance;
    }

    public void reset() {
        this.dopCore.reset();
        addMessage("Core reset");
        if (this.dopCore.getController().getActualInstruction() != null){
            setEditorDbgLine(this.dopCore.getController().getActualInstruction().getSrcLine(), file);
        }        
    }

    public void step() {
        if (this.dopCore==null){
            Toolkit.getDefaultToolkit().beep();                        
        }else{
            this.dopCore.clock();
            if (this.dopCore.getController().getActualInstruction() != null){
                setEditorDbgLine(this.dopCore.getController().getActualInstruction().getSrcLine(), file);
            }
        }
    }

    private void readMiFile(File f) {
        InputStreamReader in = null;
        String charsetName = Charset.defaultCharset().name();
        String fileName = f.getName();

        addMessage("start loading file "+fileName+"...");
        
        try {
            InputStream is = new FileInputStream(f);
            in = new InputStreamReader(is, charsetName);
            //Reader in = new FileReader(f);
            char[] buff = new char[4096];
            int nch;
            while ((nch = in.read(buff, 0, buff.length)) != -1) {
                source += new String(buff, 0, nch);
            }
        } catch (FileNotFoundException ex) {
            addError(ex.getMessage());
        } catch (UnsupportedEncodingException ex) {
            addError(ex.getMessage());
        } catch (IOException ex) {
            addError(ex.getMessage());
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                addError(ex.getMessage());
            }
        }

    }

    private void registerListeners() {
        DopDbgTopComponent.findInstance().registerListeners(this.dopCore);
    }

    private void setEditorDbgLine(int srcLine, File file) {
        Node[] nodes = TopComponent.getRegistry().getActivatedNodes();
        // Node[] nodes = TopComponent.getRegistry().getCurrentNodes();
        if (nodes == null ){
            addError( this.getClass().getName()+".setEditorDbgLine: current nodes == null (file is closed?)");
            return;
        }
        for (int i=0; i< nodes.length; i++){
            MultiDataObject d = (MultiDataObject) nodes[i].getCookie( MultiDataObject.class );
            if ( d != null ){
                FileObject f = d.getPrimaryFile();
                File nodeFile = FileUtil.toFile(f);
                
                if (nodeFile.equals(file)){
                    //System.out.println("mame nas soubor :)");
                    EditorCookie editorCookie = (EditorCookie) nodes[i].getCookie(EditorCookie.class);
                    if (editorCookie == null) return;
                    
                    StyledDocument document = editorCookie.getDocument();
                    if (document == null) return;
                    
                    JEditorPane[] jEditorPane = editorCookie.getOpenedPanes();
                    if ((jEditorPane == null) || (jEditorPane.length < 1)) return;
                    JEditorPane editorPane = jEditorPane[0];
                    int offset = NbDocument.findLineOffset(document, srcLine - 1);
                    int length = (NbDocument.findLineOffset(document, srcLine) - offset) - 2;
                    editorPane.setCaretPosition( offset );
                    
                    if (this.currentInstrAnnotacion != null){
                        try{
                            ((BaseDocument)document).getAnnotations().removeAnnotation(currentInstrAnnotacion);
                        }catch(java.lang.NullPointerException ex){}
                    }
                    currentInstrAnnotacion = new DbgAnnotation(srcLine-1, offset, -1);
                    ((BaseDocument)document).getAnnotations().addAnnotation( currentInstrAnnotacion );
                    
                                 
                }
                
            }
        }
    }

    private void setMemoryFile(File memFile) {
        this.memFile = memFile;
        memoryData = "";

        InputStreamReader in = null;
        String charsetName = Charset.defaultCharset().name();
        String fileName = memFile.getName();

        addMessage("start loading memory file "+fileName+"...");
        
        try {
            InputStream is = new FileInputStream(memFile);
            in = new InputStreamReader(is, charsetName);
            //Reader in = new FileReader(f);
            char[] buff = new char[4096];
            int nch;
            while ((nch = in.read(buff, 0, buff.length)) != -1) {
                memoryData += new String(buff, 0, nch);
            }

            try{
                addMessage("start parsing memory...");
                memory = new Memory(memoryData, this);
                dopCore.setMemory(memory);
                memory.print();
                DopDbgMemViewTopComponent.findInstance().assignMemory(memory);
            }catch(Exception ex){
                addError(ex.getMessage());            
            }
            
        } catch (FileNotFoundException ex) {
            addError(ex.getMessage());
        } catch (UnsupportedEncodingException ex) {
            addError(ex.getMessage());
        } catch (IOException ex) {
            addError(ex.getMessage());
        } finally {
            try {
                in.close();
            } catch (IOException ex) {
                addError(ex.getMessage());
            }
        }
    }


    private void setMicroinstrucionFile(File f) {
        this.file = f;
        source = "";
        
        readMiFile(f);
        
        try {

            Parser parser = new Parser(source);
            parser.run();
            addMessage("microinstruction memory usage:");
            parser.printMemory(this);
            addMessage("loading DOP core...");
            this.dopCore.setMiMemory( parser.getMemory() );
            
        } catch (SyntaxError ex) {
            addError(ex.getMessage());
        } catch (MemorySizeError ex) {
            addError(ex.getMessage());
        }
        
    }

    private void init(File miFile, File memFile, Node node) {
        this.dopCore = new Core((cz.karry.dop.Logger)this);
        registerListeners();

        changeFiles(miFile, memFile, node);

        addMessage("create DOP core...");
    }
    
    public void changeFiles(File f, File memFile, Node node) {
        clearOutput();
        
        EditorCookie editorCookie = (EditorCookie) node.getCookie (EditorCookie.class);
        if (editorCookie != null){
            editorCookie.open();
            if (editorCookie.isModified()){
                addMessage("save opened file...");
                try{
                    editorCookie.saveDocument();
                }catch( Exception ex){
                    addError(ex.getMessage());
                }
            }
        }

        this.setMicroinstrucionFile(f);
        this.setMemoryFile(memFile);

        
        reset();
    }    

    public void addMessage(final String msg) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                DopDbgOutTopComponent.findInstance().addMessage(msg+"\n");
            }
        });
    }

    public void addError(final String msg) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                DopDbgOutTopComponent.findInstance().addError(msg+"\n");
            }
        });
    }

    public void clearOutput() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                DopDbgOutTopComponent win2 = DopDbgOutTopComponent.findInstance();
                win2.clear();
            }
        });
    }


}
