package open.dolphin.client;

import open.dolphin.project.GlobalConstants;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.print.PageFormat;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import open.dolphin.project.GlobalSettings;
import open.dolphin.delegater.remote.RemoteSetaDelegater;
import open.dolphin.infomodel.IInfoModel;
import open.dolphin.infomodel.ModelUtils;
import open.dolphin.infomodel.PatientModel;
import open.dolphin.infomodel.SimpleDate;
import open.dolphin.infomodel.TouTouReply;
import open.dolphin.infomodel.UserModel;
import open.dolphin.project.GlobalVariables;
import open.dolphin.helper.IChartCommandAccepter;
import open.dolphin.utils.AgeCalculator;

/**
 * 紹介状返信　MEMO:画面
 * @author
 */
public class LetterReplyImpl extends PrintablePanel implements IChartDocument, IChartCommandAccepter {

    private static final String TITLE = "紹介患者経過報告書";
    private TouTouReply model;
    private LetterReplyView view;
    private StateMgr stateMgr;
    //元々はAbstractChartDocumentから継承
    private static final String[] CHART_MENUS = {
        GUIConst.ACTION_OPEN_KARTE, GUIConst.ACTION_SAVE, GUIConst.ACTION_DIRECTION, GUIConst.ACTION_DELETE, GUIConst.ACTION_PRINT, GUIConst.ACTION_MODIFY_KARTE,
        GUIConst.ACTION_ASCENDING, GUIConst.ACTION_DESCENDING, GUIConst.ACTION_SHOW_MODIFIED, GUIConst.ACTION_SHOW_UNSEND, GUIConst.ACTION_SHOW_SEND, GUIConst.ACTION_SHOW_NEWEST,
        GUIConst.ACTION_INSERT_TEXT, GUIConst.ACTION_INSERT_SCHEMA, GUIConst.ACTION_INSERT_STAMP, GUIConst.ACTION_SELECT_INSURANCE,
        GUIConst.ACTION_CUT, GUIConst.ACTION_COPY, GUIConst.ACTION_PASTE, GUIConst.ACTION_UNDO, GUIConst.ACTION_REDO
    };
    private IChart parent;
    private String title;
    //  protected JPanel ui;
    private boolean dirty;
    //  private Application app;
    // private ApplicationContext appCtx;
    //  private TaskMonitor taskMonitor;
    //   private TaskService taskService;

    /**
     * Creates new form LetterReplyImplNew
     * @param parent
     */
    public LetterReplyImpl(IChart parent) {
        this.title = TITLE;
        this.parent = parent;
        initComponents();

        //   appCtx = GlobalConstants.getApplicationContext();
        //    app = appCtx.getApplication();
        //   taskMonitor = appCtx.getTaskMonitor();
        //   taskService = appCtx.getTaskService();
        //   setTitle(TITLE);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 400, Short.MAX_VALUE));
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 300, Short.MAX_VALUE));
    }// </editor-fold>//GEN-END:initComponents

    /**
     * Variables declaration - do not modify//GEN-BEGIN:variables
     * End of variables declaration//GEN-END:variables
     * @return
     */
    @Override
    public TYPE getType() {
        return TYPE.LetterReplyImpl;
    }

    /**
     *
     * @param tf
     * @param value
     */
    private void setModelValue(JTextField tf, String value) {
        if (value != null) {
            tf.setText(value);
        }
    }

    /**
     *
     * @param tf
     * @return
     */
    private String getFieldValue(JTextField tf) {
        String ret = tf.getText().trim();
        if (!ret.equals("")) {
            return ret;
        }
        return null;
    }

    /**
     *
     * @param ta
     * @return
     */
    private String getAreaValue(JTextArea ta) {
        String ret = ta.getText().trim();
        if (!ret.equals("")) {
            return ret;
        }
        return null;
    }

    /**
     *
     * @param date
     * @return
     */
    private String dateToLocaleDateString(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日");
        return sdf.format(date);
    }

    /**
     *
     * @param mmlDate
     * @return
     */
    private String mmlDateStringToLocaleDateString(String mmlDate) {
        Date d = ModelUtils.getDateAsObject(mmlDate);
        return dateToLocaleDateString(d);
    }

    /**
     *
     * @param model
     */
    private void displayModel(TouTouReply model) {
        String dateStr = dateToLocaleDateString(model.getConfirmed());
        view.getConfirmed().setText(dateStr);
        view.getVisited().setText(model.getVisited());

        String birthdayStr = mmlDateStringToLocaleDateString(model.getPatientBirthday());

        setModelValue(view.getPatientName(), model.getPatientName());
        setModelValue(view.getPatientBirthday(), birthdayStr);
        setModelValue(view.getPatientName(), model.getPatientName());
    }

    /**
     *
     * @param model
     */
    private void restore(TouTouReply model) {

        Date d = new Date();
        model.setConfirmed(d);
        model.setRecorded(d);
        model.setStarted(d);
        model.setStatus(IInfoModel.STATUS_FINAL);
        model.setKarte(getParentContext().getKarte());
        model.setCreator(GlobalVariables.getUserModel());

        model.setClientHospital(getFieldValue(view.getClientHospital()));
        model.setClientDept(getFieldValue(view.getClientDept()));
        model.setClientDoctor(getFieldValue(view.getClientDoctor()));
        String visited = getFieldValue(view.getVisited());
        model.setVisited(visited);
        model.setInformedContent(getAreaValue(view.getInformedContent()));
    }

    /**
     *
     */
    @Override
    public void start() {

        // 紹介状モデルを生成する
        this.model = new TouTouReply();

        // 確定日として現在を表紙させる
        Date d = new Date();
        this.model.setConfirmed(d);
        model.setVisited(ModelUtils.getDateAsFormatString(d, "yyyy-MM-dd"));

        PatientModel patient = getParentContext().getPatient();
        this.model.setPatientName(patient.getFullName());
        this.model.setPatientGender(patient.getGenderDesc());
        this.model.setPatientBirthday(patient.getBirthday());
        this.model.setPatientAge(AgeCalculator.getAge(patient.getBirthday()));

        UserModel user = GlobalVariables.getUserModel();
        this.model.setConsultantHospital(user.getFacility().getFacilityName());
        this.model.setConsultantDoctor(user.getCommonName());

        // view を生成する
        this.view = new LetterReplyView();
        JScrollPane scroller = new JScrollPane(this.view);
        scroller.getVerticalScrollBar().setUnitIncrement(GlobalSettings.karteScrollUnitIncrement());
        setLayout(new BorderLayout());
        add(scroller);

        // モデルを表示する
        displayModel(this.model);

        //
        // DirtyListener を登録する
        //
        DocumentListener dl = new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent documentEvent) {
                stateMgr.processDirtyEvent();
            }

            @Override
            public void removeUpdate(DocumentEvent documentEvent) {
                stateMgr.processDirtyEvent();
            }

            @Override
            public void changedUpdate(DocumentEvent documentEvent) {
                stateMgr.processDirtyEvent();
            }
        };
        view.getClientHospital().getDocument().addDocumentListener(dl);
        view.getClientDept().getDocument().addDocumentListener(dl);
        view.getClientDoctor().getDocument().addDocumentListener(dl);
        view.getVisited().getDocument().addDocumentListener(dl);
        view.getInformedContent().getDocument().addDocumentListener(dl);


        view.getClientHospital().addFocusListener(AutoKanjiListener.getInstance());
        view.getClientDept().addFocusListener(AutoKanjiListener.getInstance());
        view.getClientDoctor().addFocusListener(AutoKanjiListener.getInstance());
        view.getVisited().addFocusListener(AutoRomanListener.getInstance());
        view.getInformedContent().addFocusListener(AutoKanjiListener.getInstance());


        PopupListener popupListener = new PopupListener(view.getVisited());//MEMO: unused?

        // 状態制御を開始する
        stateMgr = new StateMgr();
    }

    /**
     *
     */
    @Override
    public void stop() {
    }

    /**
     *
     * @return
     */
    @Override
    public boolean prepare() {
        return true;
    }

    /**
     *
     * @return
     */
    private boolean save() {

        restore(this.model);

        RemoteSetaDelegater ddl = new RemoteSetaDelegater();
        long result = ddl.saveOrUpdateLetter(model);
        if (!ddl.isError()) {
            model.setId(result);
        }
        stateMgr.processSavedEvent();

        getParentContext().getDocumentHistory().getDocumentHistory();
        closeEditor();
        return true;
    }

    /*
     * ウィンドウまたはタブを閉じる。新規ボタンをEnableする。
     * */
    private void closeEditor() {

        getParentContext().enabledAction(GUIConst.ACTION_NEW_DOCUMENT, true);
        getParentContext().enabledAction(GUIConst.ACTION_DIRECTION, false);

        //  getParentContext().setNewButtonEnabled(true);
        //   getParentContext().setDirectionButtonEnabled(false);
        getParentContext().closeChartDocument(title, this);
        getParentContext().getChartMediator().unsetAccepter(this);
    }

    /**
     *
     */
    @Override
    public void enter() {

        getParentContext().getStatusPanel().setMessage("");
        getParentContext().getChartMediator().setAccepter(this);
        disableMenus();

        getParentContext().enabledAction(GUIConst.ACTION_NEW_KARTE, true);
        getParentContext().enabledAction(GUIConst.ACTION_NEW_DOCUMENT, true);

        if (stateMgr != null) {
            stateMgr.enter();
        }
    }

    /**
     *
     * @return
     */
    private boolean print() {
        //command
        if (this.model == null) {
            return true;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("PDFファイルを作成しますか?");

        int option = JOptionPane.showOptionDialog(
                getParentContext().getFrame(),
                sb.toString(),
                GlobalConstants.getFrameTitle("紹介状印刷"),
                JOptionPane.DEFAULT_OPTION,
                JOptionPane.INFORMATION_MESSAGE,
                null,
                new String[]{"PDF作成", "フォーム印刷", "取消し"},
                "PDF作成");

        if (option == 0) {
            save();
            makePDF();
        } else if (option == 1) {
            PageFormat pageFormat = getParentContext().getContext().getPageFormat();
            String name = getParentContext().getPatient().getFullName();
            PrintablePanel panel = (PrintablePanel) this.view;
            panel.printPanel(pageFormat, 1, false, name, 0);
        }
        return true;
    }

    /**
     *
     */
    public void makePDF() {

        if (this.model == null) {
            return;
        }

        Runnable r = new Runnable() {

            @Override
            public void run() {

                PDFReplyMaker pdf = new PDFReplyMaker();
                String pdfDir = GlobalVariables.getPreferences().get("pdfStore", System.getProperty("user.dir"));
                pdf.setDocumentDir(pdfDir);
                pdf.setModel(model);
                final boolean result = pdf.create();
                final String fileName = pdf.getFileName();
                final String dir = pdf.getDocumentDir();

                Runnable awt = new Runnable() {

                    @Override
                    public void run() {
                        if (result) {
                            StringBuilder sb = new StringBuilder();
                            //String fileName = pdf.getFileName();
                            //String dir = pdf.getDocumentDir();
                            sb.append(fileName);
                            sb.append("を");
                            sb.append(System.getProperty("line.separator"));
                            sb.append(dir);
                            sb.append("に保存しました。");
                            sb.append(System.getProperty("line.separator"));
                            sb.append("PDF ビュワーを起動し印刷してください。");
                            JOptionPane.showMessageDialog(
                                    getParentContext().getFrame(),
                                    sb.toString(),
                                    GlobalConstants.getFrameTitle("紹介状作成"),
                                    JOptionPane.INFORMATION_MESSAGE);

                        } else {
                            JOptionPane.showMessageDialog(
                                    getParentContext().getFrame(),
                                    "紹介状PDFファイルを生成することができません。",
                                    GlobalConstants.getFrameTitle("紹介状作成"),
                                    JOptionPane.WARNING_MESSAGE);
                        }
                    }
                };
                EventQueue.invokeLater(awt);
            }
        };
        Thread t = new Thread(r);
        t.setPriority(Thread.NORM_PRIORITY);
        t.start();
    }

    /**
     *
     * @return
     */
    @Override
    public boolean isDirty() {
        if (stateMgr != null) {
            return stateMgr.isDirtyState();
        } else {
            return dirty;
        }
    }

    /**
     *
     * @param command
     * @return
     */
    @Override
    public boolean dispatchChartCommand(open.dolphin.helper.IChartCommandAccepter.ChartCommand command) {
        switch (command) {
            case modifyKarte:
                return modifyKarte();
            case save:
                return save();
            case print:
                return print();
            default:
        }
        return false;
    }

    /**
     * 紹介状を変更する。
     */
    private boolean modifyKarte() {
        stateMgr.processModifyKarteEvent();
        return true;
    }

    /**
     *
     * @param b
     */
    private void setEditables(boolean b) {
        view.getClientHospital().setEditable(b);
        view.getClientDept().setEditable(b);
        view.getClientDoctor().setEditable(b);
        view.getVisited().setEditable(b);
        view.getInformedContent().setEditable(b);
    }

    /**
     *
     * @return
     */
    @Override
    public boolean itLayoutSaved() {
        return false;
    }
    //元々はAbstractChartDocumentから継承

    /**
     *
     * @return
     */
    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public IChart getParentContext() {
        return parent;
    }

    @Override
    public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    /**
     *
     * @return
     */
    public boolean isReadOnly() {
        return getParentContext().isReadOnly();
    }

    /**
     *
     */
    public void disableMenus() {
        // このウインドウに関連する全てのメニューをdisableにする
        ChartMediator mediator = getParentContext().getChartMediator();
        mediator.disableMenus(CHART_MENUS);
    }

    /**
     * 共通の警告表示を行う。
     * @param title 
     * @param message
     */
    protected void warning(String title, String message) {
        Window parentWindow = SwingUtilities.getWindowAncestor(this);
        JOptionPane.showMessageDialog(parentWindow, message, GlobalConstants.getFrameTitle(title), JOptionPane.WARNING_MESSAGE);
    }

    /**
     *
     * @return
     */
    @Override
    public List<JTabbedPane> getTabbedPanels() {
        return null;
    }

    /**
     *
     * @param o
     * @return
     */
    @Override
    public boolean update(Object o) {
        return true;
    }

    /**
     *
     */
    abstract class State {

        public abstract void enter();
    }

    /**
     * Claen State クラス。
     */
    class EmptyState extends State {

        /**
         *
         */
        EmptyState() {
        }

        /**
         *
         */
        @Override
        public void enter() {
            getParentContext().enabledAction(GUIConst.ACTION_SAVE, false);
            getParentContext().enabledAction(GUIConst.ACTION_PRINT, false);
            getParentContext().enabledAction(GUIConst.ACTION_DIRECTION, false);
            getParentContext().enabledAction(GUIConst.ACTION_MODIFY_KARTE, false);
            getParentContext().enabledAction(GUIConst.ACTION_ADD_USER, GlobalVariables.isAdmin());
        }
    }

    /**
     * Dirty State クラス。
     */
    class DirtyState extends State {

        /**
         *
         */
        DirtyState() {
        }

        /**
         *
         */
        @Override
        public void enter() {
            getParentContext().enabledAction(GUIConst.ACTION_SAVE, true);
            getParentContext().enabledAction(GUIConst.ACTION_PRINT, true);
            getParentContext().enabledAction(GUIConst.ACTION_DIRECTION, true);
            getParentContext().enabledAction(GUIConst.ACTION_MODIFY_KARTE, false);
            getParentContext().enabledAction(GUIConst.ACTION_ADD_USER, GlobalVariables.isAdmin());
        }
    }

    /**
     * Saved State クラス。
     */
    class CleanState extends State {

        /**
         *
         */
        CleanState() {
        }

        /**
         *
         */
        @Override
        public void enter() {
            setEditables(false);
            getParentContext().enabledAction(GUIConst.ACTION_SAVE, false);
            getParentContext().enabledAction(GUIConst.ACTION_PRINT, true);
            getParentContext().enabledAction(GUIConst.ACTION_DIRECTION, true);
            getParentContext().enabledAction(GUIConst.ACTION_MODIFY_KARTE, true);
            getParentContext().enabledAction(GUIConst.ACTION_ADD_USER, GlobalVariables.isAdmin());
        }
    }

    /**
     *
     */
    class StartEditingState extends State {

        /**
         *
         */
        StartEditingState() {
        }

        /**
         *
         */
        @Override
        public void enter() {
            setEditables(true);
            getParentContext().enabledAction(GUIConst.ACTION_SAVE, false);
            getParentContext().enabledAction(GUIConst.ACTION_PRINT, true);
            getParentContext().enabledAction(GUIConst.ACTION_DIRECTION, true);
            getParentContext().enabledAction(GUIConst.ACTION_MODIFY_KARTE, false);
            getParentContext().enabledAction(GUIConst.ACTION_ADD_USER, GlobalVariables.isAdmin());
        }
    }

    /**
     * State の Context クラス。
     */
    class StateMgr {

        private EmptyState emptyState = new EmptyState();
        private DirtyState dirtyState = new DirtyState();
        private CleanState cleanState = new CleanState();
        private StartEditingState startEditingState = new StartEditingState();
        private State curState;

        /**
         *
         */
        public StateMgr() {
            curState = emptyState;
            enter();
        }

        /**
         *
         */
        public void enter() {
            curState.enter();
        }

        /**
         *
         */
        public void processDirtyEvent() {

            boolean newDirty = (getFieldValue(view.getClientHospital()) != null && getAreaValue(view.getInformedContent()) != null) ? true : false;
            if (isDirtyState() != newDirty) {
                curState = newDirty ? dirtyState : emptyState;
                curState.enter();
            }
        }

        /**
         *
         */
        public void processSavedEvent() {
            curState = cleanState;
            curState.enter();
        }

        /**
         *
         */
        public void processModifyKarteEvent() {
            curState = startEditingState;
            curState.enter();
        }

        /**
         *
         * @return
         */
        public boolean isDirtyState() {
            return curState == dirtyState ? true : false;
        }
    }

    /**
     *　MEMO:リスナー
     */
    class PopupListener extends MouseAdapter implements PropertyChangeListener {

        private JPopupMenu popup;
        private JTextField tf;

        /**
         *
         * @param tf
         */
        public PopupListener(JTextField tf) {
            this.tf = tf;
            tf.addMouseListener(this);
        }

        /**
         *
         * @param e
         */
        @Override
        public void mousePressed(MouseEvent e) {
            maybeShowPopup(e);
        }

        /**
         *
         * @param e
         */
        @Override
        public void mouseReleased(MouseEvent e) {
            maybeShowPopup(e);
        }

        /**
         *
         * @param e
         */
        private void maybeShowPopup(MouseEvent e) {

            if (e.isPopupTrigger()) {
                popup = new JPopupMenu();
                CalendarCardPanel cc = new CalendarCardPanel(GlobalConstants.getEventColorTable());
                cc.addPropertyChangeListener(CalendarCardPanel.PICKED_DATE, this);
                cc.setCalendarRange(new int[]{-12, 0});
                popup.insert(cc, 0);
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
        }

        /**
         *
         * @param e
         */
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            if (e.getPropertyName().equals(CalendarCardPanel.PICKED_DATE)) {
                SimpleDate sd = (SimpleDate) e.getNewValue();
                String mmldate = SimpleDate.simpleDateToMmldate(sd);
                tf.setText(mmldate);
                popup.setVisible(false);
                popup = null;
            }
        }
    }
}
