package jp.ac.do_johodai.j314.sw.jc;

import jp.ac.do_johodai.j314.sw.je.*;
import jp.ac.do_johodai.j314.sw.ws.*;

import javax.swing.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.awt.*;
import java.io.*;
import java.net.*;

import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.query.*;

public class JenaFrame extends JFrame implements ActionListener, JEListener {
    private static final long serialVersionUID = 1L;
    JenaCheck jc;
    ServerWrapper serverWrapper;
    JenaEngine engine;
    long timerInterval = 1000;
    int port = 8080;
    JMenuBar menuBar;
    JMenu fileMenu;
    JMenuItem loadRDFMI;
    JMenuItem loadRulesMI;
    JMenuItem saveRDFasN3MI;
    JMenuItem saveRulesMI;
    JMenuItem timerIntervalMI;
    JMenuItem setWSPortMI;
    JMenuItem startWebServiceMI;
    JMenuItem stopWebServiceMI;
    JMenuItem quitMI;
    JButton clearRDFButton;
    JButton resetRDFButton;
    JCheckBox traceCB;
    JCheckBox timerCB;
    JCheckBox owlDLCB;
    JComboBox reasonerModeCB;
    JTabbedPane tabP;
    JTextArea rulesTA;
    JButton setRulesButton;
    JButton resetRulesButton;
    JTextArea notation3TA;
    JButton printNotation3Button;
    JButton addNotation3Button;
    JButton delNotation3Button;
    JButton resetNotation3Button;
    JTextArea sparqlTA;
    JButton execSparqlButton;
    JButton resetSparqlButton;
    JButton insSparqlButton;
    JButton delSparqlButton;
    JTextField inputTF;
    JTextField inputTF2;
    JTextArea outputTA;
    JButton clearOutputButton;

    public JenaFrame(JenaCheck jc) {
        super("JenaCheck");
        this.jc = jc;

        menuBar = new JMenuBar();
        this.setJMenuBar(menuBar);
        fileMenu = new JMenu("ファイル");
        menuBar.add(fileMenu);
        loadRDFMI = new JMenuItem("RDFをロード");
        loadRulesMI = new JMenuItem("ルールをロード");
        saveRDFasN3MI = new JMenuItem("RDFをN3で保存");
        saveRulesMI = new JMenuItem("ルールを保存");
        timerIntervalMI = new JMenuItem("タイマーのインターバルをセット");
        setWSPortMI = new JMenuItem("WebServiceのポートを指定する");
        startWebServiceMI = new JMenuItem("WebServiceを起動する");
        stopWebServiceMI = new JMenuItem("WebServiceを停止する");
        quitMI = new JMenuItem("終了する");
        loadRDFMI.addActionListener(this);
        loadRulesMI.addActionListener(this);
        saveRDFasN3MI.addActionListener(this);
        saveRulesMI.addActionListener(this);
        timerIntervalMI.addActionListener(this);
        setWSPortMI.addActionListener(this);
        startWebServiceMI.addActionListener(this);
        stopWebServiceMI.addActionListener(this);
        quitMI.addActionListener(this);
        fileMenu.add(loadRDFMI);
        fileMenu.add(loadRulesMI);
        fileMenu.add(saveRDFasN3MI);
        fileMenu.add(saveRulesMI);
        fileMenu.add(timerIntervalMI);
        fileMenu.add(setWSPortMI);
        fileMenu.add(startWebServiceMI);
        fileMenu.add(stopWebServiceMI);
        fileMenu.add(quitMI);

        Font f = new Font("MonoSpaced",Font.PLAIN,12);
        clearRDFButton = new JButton("RDFモデルを空にする");
        resetRDFButton = new JButton("RDFモデルを推論前にもどす");
        traceCB = new JCheckBox("トレース");
        timerCB = new JCheckBox("タイマー");
        owlDLCB = new JCheckBox("OWL-DL");
        String s[] = {"hybrid","forward","forwardRETE","backward"};
        reasonerModeCB = new JComboBox(s);
        tabP = new JTabbedPane();
        rulesTA = new JTextArea(10, 60);
        rulesTA.setFont(f);
        setRulesButton = new JButton("ルールをセット");
        resetRulesButton = new JButton("リセット");
        notation3TA = new JTextArea(15, 60);
        notation3TA.setFont(f);
        printNotation3Button = new JButton("表示");
        addNotation3Button = new JButton("追加");
        delNotation3Button = new JButton("削除");
        resetNotation3Button = new JButton("クリア");
        sparqlTA = new JTextArea(15, 60);
        sparqlTA.setFont(f);
        execSparqlButton = new JButton("検索");
        insSparqlButton = new JButton("追加");
        delSparqlButton = new JButton("削除");
        resetSparqlButton = new JButton("クリア");
        inputTF = new JTextField();
        inputTF.setFont(f);
        inputTF.setMaximumSize(new Dimension(10000,30));
        inputTF2 = new JTextField();
        inputTF2.setFont(f);
        inputTF2.setMaximumSize(new Dimension(10000,30));
        outputTA = new JTextArea(15, 60);
        outputTA.setFont(f);
        clearOutputButton = new JButton("標準出力をクリア");

        JTextAreaOutputStream jtaos = new JTextAreaOutputStream(outputTA,
                System.out);
        PrintStream ps = new PrintStream(jtaos, true);
        System.setOut(ps);
        System.setErr(ps);
        JTextFieldInputStream jtfis = new JTextFieldInputStream(inputTF);
        System.setIn(jtfis); // <- [A]この行は下の[B]より上にないと微妙にだめ(処理系に依存するかも)

        engine = new JenaEngine();
        // 有効にしたいビルトインを追加していきます。
        // 代入と標準入力ビルトイン
        engine.registerBuiltin(new BindBuiltin());
        engine.registerBuiltin(new ReadBuiltin());
        engine.registerBuiltin(new RandIntBuiltin());
        engine.registerBuiltin(new String2IntBuiltin());
        engine.registerBuiltin(new ListBuiltin());
        engine.registerBuiltin(new ConsBuiltin());
        engine.registerBuiltin(new List2StringBuiltin());
        engine.registerBuiltin(new ListAppendBuiltin());
        engine.registerBuiltin(new PrintListBuiltin());
        engine.registerBuiltin(new StartsWithBuiltin());
        engine.registerBuiltin(new EndsWithBuiltin());
        engine.registerBuiltin(new NotStartsWithBuiltin());
        engine.registerBuiltin(new NotEndsWithBuiltin());
        engine.registerBuiltin(new StringConcatBuiltin());
        engine.registerBuiltin(new StringFormatBuiltin());
        engine.registerBuiltin(new Int2StringBuiltin());
        engine.registerBuiltin(new StateSetBuiltin());
        engine.registerBuiltin(new StateGetBuiltin());
        engine.registerBuiltin(new StatePrintBuiltin());
        engine.registerBuiltin(new SMailBuiltin());
        engine.registerBuiltin(new Long2DateTimeBuiltin());
        engine.registerBuiltin(new DateTime2LongBuiltin());
        // Axis2に依存するビルトイン
        engine.registerBuiltin(new SendN3Builtin());
        engine.registerBuiltin(new SendN3RESTBuiltin());
        // a23に依存するビルトイン
        A3NewBuiltin a3new = new A3NewBuiltin();
        a3new.setJenaEngine(engine);
        engine.registerBuiltin(a3new);
        engine.registerBuiltin(new A3SetVisibleBuiltin());
        engine.registerBuiltin(new A3ChangeBuiltin());
        engine.registerBuiltin(new A3PrintBuiltin());
        engine.registerBuiltin(new A3ReadBuiltin());
        engine.registerBuiltin(new A3YesNoBuiltin());
        // 形態素解析に関するビルトイン(senに依存)
        engine.registerBuiltin(new KeitaisoInitBuiltin());
        engine.registerBuiltin(new KeitaisoBuiltin());
        engine.registerBuiltin(new KeitaisokaisekiBuiltin());

        serverWrapper = new ServerWrapper(engine);
        serverWrapper.setPort(port);
        //serverWrapper.start();

        Box baseBox = Box.createVerticalBox();
        Box confBox = Box.createHorizontalBox();
        Box rulesBox = Box.createVerticalBox();
        Box rulesBBox = Box.createHorizontalBox();
        Box n3Box = Box.createVerticalBox();
        Box n3BBox = Box.createHorizontalBox();
        Box sparqlBox = Box.createVerticalBox();
        Box sparqlBBox = Box.createHorizontalBox();
        Box inputBox = Box.createHorizontalBox();
        Box inputBox2 = Box.createHorizontalBox();
        Box outputBox = Box.createVerticalBox();
        Border b = new LineBorder(Color.black);
        confBox.setBorder(new TitledBorder(b, "設定とか"));
        rulesBox.setBorder(new TitledBorder(b, "ルール"));
        n3Box.setBorder(new TitledBorder(b, "Notation3"));
        sparqlBox.setBorder(new TitledBorder(b, "SPARQL"));
        inputBox.setBorder(new TitledBorder(b, "標準入力"));
        inputBox2.setBorder(new TitledBorder(b, "三組入力"));
        outputBox.setBorder(new TitledBorder(b, "標準出力"));
        baseBox.add(confBox);
        baseBox.add(tabP);
        tabP.add("ルール",rulesBox);
        tabP.add("Notation3",n3Box);
        tabP.add("SPARQL",sparqlBox);
        baseBox.add(inputBox);
        baseBox.add(inputBox2);
        baseBox.add(outputBox);
        this.getContentPane().add(baseBox);

        confBox.add(clearRDFButton);
        confBox.add(resetRDFButton);
        confBox.add(traceCB);
        confBox.add(timerCB);
        confBox.add(owlDLCB);
        confBox.add(reasonerModeCB);
        confBox.add(Box.createHorizontalGlue());
        rulesBox.add(new JScrollPane(rulesTA));
        rulesBBox.add(setRulesButton);
        rulesBBox.add(resetRulesButton);
        n3Box.add(new JScrollPane(notation3TA));
        n3BBox.add(printNotation3Button);
        n3BBox.add(addNotation3Button);
        n3BBox.add(delNotation3Button);
        n3BBox.add(resetNotation3Button);
        sparqlBox.add(new JScrollPane(sparqlTA));
        sparqlBBox.add(execSparqlButton);
        sparqlBBox.add(insSparqlButton);
        sparqlBBox.add(delSparqlButton);
        sparqlBBox.add(resetSparqlButton);
        inputBox.add(inputTF);
        inputBox2.add(inputTF2);
        outputBox.add(new JScrollPane(outputTA));
        outputBox.add(clearOutputButton);

        rulesBox.add(rulesBBox);
        n3Box.add(n3BBox);
        sparqlBox.add(sparqlBBox);

        clearRDFButton.addActionListener(this);
        resetRDFButton.addActionListener(this);
        traceCB.addActionListener(this);
        timerCB.addActionListener(this);
        owlDLCB.addActionListener(this);
        reasonerModeCB.addActionListener(this);
        setRulesButton.addActionListener(this);
        resetRulesButton.addActionListener(this);
        printNotation3Button.addActionListener(this);
        addNotation3Button.addActionListener(this);
        delNotation3Button.addActionListener(this);
        resetNotation3Button.addActionListener(this);
        execSparqlButton.addActionListener(this);
        insSparqlButton.addActionListener(this);
        delSparqlButton.addActionListener(this);
        resetSparqlButton.addActionListener(this);
        inputTF.addActionListener(this);// <-[B]この行は上の[A]より下にないと微妙にだめ(処理系に依存するかも)
        inputTF2.addActionListener(this);
        clearOutputButton.addActionListener(this);
    }

    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() == loadRDFMI) {
            String s = JOptionPane.showInputDialog(this, "input rdf URL:");
            if (s != null) {
                JEMessage m = new JEMessage(JEMessage.Type.LOAD_RDF, this, s);
                engine.recieveRequest(m);
            }
        } else if (ae.getSource() == loadRulesMI) {
            try {
                String s = JOptionPane
                        .showInputDialog(this, "input rules URL:");
                if (s != null) {
                    URL url = new URL(s);
                    InputStreamReader isr = new InputStreamReader(url
                            .openStream(), "utf-8");
                    BufferedReader br = new BufferedReader(isr);
                    rulesTA.setText("");
                    String line;
                    while ((line = br.readLine()) != null) {
                        rulesTA.append(line + "\n");
                    }
                }
                JEMessage m = new JEMessage(JEMessage.Type.SET_RULES, this,
                        rulesTA.getText());
                engine.recieveRequest(m);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (ae.getSource() == saveRDFasN3MI) {
            String s = JOptionPane.showInputDialog(this, "save rdf URL:");
            if (s != null) {
                String query = "CONSTRUCT{?x ?y ?z}WHERE{?x ?y ?z}";
                JEMessage m = new JEMessage(JEMessage.Type.SPARQL, this, query);
                try {
                    m.setUserData(new URI(s));
                    engine.recieveRequest(m);
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }
            }
        } else if (ae.getSource() == saveRulesMI) {
            try {
                String s = JOptionPane.showInputDialog(this, "save rules URL:");
                if (s != null) {
                    URI uri = new URI(s);
                    PrintStream ps = new PrintStream(new File(uri), "UTF-8");
                    ps.println(rulesTA.getText());
                    ps.close();
                    System.out.println("(テキストエリアの)ルールを" + s + "に保存しました。");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (ae.getSource() == timerIntervalMI) {
            String s = JOptionPane.showInputDialog(this,"input timer interval(ms):");
            if (s != null) {
                timerInterval = Long.parseLong(s);
                System.out.println("タイマーのインターバルを"+timerInterval+"(ms)に変更しました。");
            }
        } else if (ae.getSource() == setWSPortMI) {
            String s = JOptionPane.showInputDialog(this,"input WebService port number:");
            if (s != null) {
                port = Integer.parseInt(s);
                serverWrapper.setPort(port);
                System.out.println("WebServiceのポートを"+port+"に変更しました。");
            }
        } else if (ae.getSource() == startWebServiceMI) {
            serverWrapper.start();
            System.out.println("WebServiceを起動させました。");
        } else if (ae.getSource() == stopWebServiceMI) {
            serverWrapper.stop();
            System.out.println("WebServiceを停止させました。");
        } else if (ae.getSource() == quitMI) {
            jc.disposeThis();
        } else if (ae.getSource() == clearRDFButton) {
            engine.clearRDF();
            System.out.println("RDFモデルを空にしました．");
        } else if (ae.getSource() == resetRDFButton) {
            engine.resetRDF();
            System.out.println("RDFモデルを推論前の状態にもどしました．");
        } else if (ae.getSource() == traceCB) {
            engine.setTrace(traceCB.isSelected());
            if (traceCB.isSelected())
                System.out.println("トレース表示をONにします．");
            else
                System.out.println("トレース表示をOFFにします．");
        } else if (ae.getSource() == timerCB) {
            JEMessage m = null;
            if (timerCB.isSelected())
                m = new JEMessage(JEMessage.Type.SET_TIMER, this, true,
                        timerInterval);
            else
                m = new JEMessage(JEMessage.Type.SET_TIMER, this, false,
                        timerInterval);
            engine.recieveRequest(m);
        } else if (ae.getSource() == owlDLCB) {
            engine.setEnableOWLDL(owlDLCB.isSelected());
            if (owlDLCB.isSelected()) {
                System.out.println("OWL-DL Reasonerを有効にします。");
            } else {
                System.out.println("OWL-DL Reasonerを無効にします。");
            }
        } else if (ae.getSource() == reasonerModeCB) {
            String mode = (String)reasonerModeCB.getSelectedItem();
            engine.setReasonerMode(mode);
            System.out.println("Reasonerの推論モードを["+mode+"]にします。");
        } else if (ae.getSource() == setRulesButton) {
            JEMessage m = new JEMessage(JEMessage.Type.SET_RULES, this, rulesTA
                    .getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == resetRulesButton) {
            rulesTA.setText("");
            JEMessage m = new JEMessage(JEMessage.Type.SET_RULES, this, rulesTA
                    .getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == printNotation3Button) {
            JEMessage m = new JEMessage(JEMessage.Type.SPARQL, this,
                    "CONSTRUCT {?x ?y ?z} WHERE {?x ?y ?z}");
            m.setUserData(sparqlTA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == addNotation3Button) {
            JEMessage m = new JEMessage(JEMessage.Type.INSERT_N3, this,
                    notation3TA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == delNotation3Button) {
            JEMessage m = new JEMessage(JEMessage.Type.DELETE_N3, this,
                    notation3TA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == resetNotation3Button) {
            notation3TA.setText("");
        } else if (ae.getSource() == execSparqlButton) {
            JEMessage m = new JEMessage(JEMessage.Type.SPARQL, this, sparqlTA
                    .getText());
            m.setUserData(sparqlTA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == insSparqlButton) {
            JEMessage m = new JEMessage(JEMessage.Type.INS_SPARQL,this,sparqlTA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == delSparqlButton) {
            JEMessage m = new JEMessage(JEMessage.Type.DEL_SPARQL,this,sparqlTA.getText());
            engine.recieveRequest(m);
        } else if (ae.getSource() == resetSparqlButton) {
            sparqlTA.setText("");
        } else if (ae.getSource() == inputTF) {
            String s = inputTF.getText();
            System.out.println(s); // <-これ微妙(処理系に依存するかも)
            // inputTF.setText("");
        } else if (ae.getSource() == inputTF2) {
            String s = inputTF2.getText();
            System.out.println(s);
            inputTF2.setText("");
            JEMessage m = new JEMessage(JEMessage.Type.SET_INPUT,this,s);
            engine.recieveRequest(m);
        } else if (ae.getSource() == clearOutputButton) {
            outputTA.setText("");
        } else {
            System.out.println(ae.toString());
        }
    }

    public void performResultMessage(JEMessage m) {
        if (m.getType() == JEMessage.Type.INSERT_N3) {
            if (((Boolean) m.getResult()).booleanValue())
                System.out.println("RDFモデルに追加しました．");
            else
                System.out.println("RDFモデルへの追加に失敗しました．");
        } else if (m.getType() == JEMessage.Type.DELETE_N3) {
            if (((Boolean) m.getResult()).booleanValue())
                System.out.println("RDFモデルから削除しました．");
            else
                System.out.println("RDFモデルからの削除に失敗しました．");
        } else if (m.getType() == JEMessage.Type.SET_TIMER) {
            if (((Boolean) m.getResult()).booleanValue())
                System.out.println("タイマーをセットしました．");
            else
                System.out.println("タイマー止めました．");
        } else if (m.getType() == JEMessage.Type.SET_RULES) {
            if (((Boolean) m.getResult()).booleanValue())
                System.out.println("ルールを空にしてからセットしました。");
            else
                System.out.println("ルールのセットに失敗しました。");
        } else if (m.getType() == JEMessage.Type.LOAD_RDF) {
            if (((Boolean) m.getResult()).booleanValue())
                System.out.println("RDFモデルを空にしてから再読み込みしました．");
            else
                System.out.println("RDFの読み込みに失敗しました。");
        } else if (m.getType() == JEMessage.Type.SPARQL) {
            Object result = m.getResult();
            if (result instanceof ResultSet) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                if (m.getUserData() instanceof String) {
                    Query q = QueryFactory.create((String)m.getUserData());
                    ResultSetFormatter.out(baos, (ResultSet) result,q);
                } else {
                    ResultSetFormatter.out(baos, (ResultSet) result);
                }
                try {
                    System.out.println(baos.toString("UTF-8"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (result instanceof Model) {
                try {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ((Model) result).write(baos, "N3");
                    if (m.getUserData() instanceof URI) {
                        URI uri = (URI) m.getUserData();
                        PrintStream ps = new PrintStream(new File(uri), "UTF-8");
                        ps.print(baos.toString("UTF-8"));
                        ps.close();
                        System.out
                                .println("RDFを" + uri.toString() + "に保存しました。");
                    } else if (m.getUserData() instanceof String) {
                        System.out.println(baos.toString("UTF-8"));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (result instanceof Boolean) {
                System.out.println(result);
            } else {
                System.out.println("???");
            }
        } else if (m.getType() == JEMessage.Type.INS_SPARQL) {
            System.out.println("以下の内容を追加しました。");
            String res = (String)m.getResult();
            System.out.println(res);
        } else if (m.getType() == JEMessage.Type.DEL_SPARQL) {
            System.out.println("以下の内容を削除しました。");
            String res = (String)m.getResult();
            System.out.println(res);
        } else if (m.getType() == JEMessage.Type.SET_INPUT) {
            //String res = (String)m.getResult();
            //System.out.println(res);
        } else {
            System.out.println("***???***");
        }
    }
}
