#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
2次元プロッタの CUI ファサードクラス
"""
import os
import sys
import thread
from math import pi, sqrt, cos
from numpy import array, arange,meshgrid, zeros, float64
from time import sleep
from vis.WxCom import *
from vis.UtilPlot import *
try:
    from Manyo import *
except:
    pass
    
#######################################
#  M2Plot
####################################### 
class M2Plot(object) :
    """
    2次元プロッタのファサードクラス
    Python インタープリタ からプロッタを起動し
    制御するためのユーザインターフェイスを提供
    """
    # 2Dプロッタ通信クラスのインスタンスリスト
    plotList = []
    # plotList のロック用
    lck = thread.allocate_lock()
  
    #########################################
    def __init__(self, matrix, closeFlag = True):
        """
        コンストラクタ
        @param  matrix  2Dプロッタに表示するデータ、省略可
        　　　　　　　(エレメントコンテナマトリックス)
        @closeFlag   親プロセス終了時に、プロッタをクローズするか
        　　　　　　 否かのフラグ
        @retval 無し
        """
        self.plot = None
        # 入力チェッククラスのインスタンスを取得、メッセージ出力先は標準出力を指定
        self.val = Validator(None)
        # テンポラリファイルクラスのインスタンス取得
        self.tp = TempFile()
        # プロッタの作成
        self.NewPlot(matrix, closeFlag)
        
    ######################################### 
    def NewPlot(self, matrix, closeFlag = True): 
        """
        プロッタを追加し、カレントプロッタとする
        @param  data  プロッタに表示するデータ、省略可
        　　　　       ???
        @param closeFlag  親プロセス終了時に、子プロセスを終了させるかどうかのフラグ
        @retval 無し
        """    
        # プロッタNo.を取得
        pno = len(self.plotList) +1
        strPno = "%d" % pno
        
        if closeFlag == True:
            cflag = "True"
        else:
            cflag = "False"
        
        arg = ["D2Vis.py",strPno, cflag]

        # プロセスID とプロッタ番号からポート番号を生成
        pxx = (os.getpid() % 500)*20
        portno = 20000+ pxx + pno

        # GUI通信クラスのインスタンス取得
        p = CommWx()
        
        # プロセス生成と通信接続に成功したなら
        if p.NewComm(portno, arg):
            # 旧カレントプロッタのアイコン化要求
            self._IconizeCurrent()
            
            self.lck.acquire()
            # 追加したプロッタをリストへ追加
            self.plotList.append(p)
            # ロックの解放
            self.lck.release()
                
            # 追加したプロッタをカレントプロッタとする。   
            self.plot = p
            # データを設定       
            self.ChangeMap(matrix, True)
                
        else:
            print "\nError!!  Cannot create a new plotter!!."
    
    ######################################### 
    def ChangePlot(self, plot=None): 
        """
        カレントプロッタを変更する
        @param  plot  カレントにするプロッタ番号
        @retval 無し
        """
        # 引数が指定されていない場合
        if not self.val.ValidateObject(plot, "ChangePlot"):
            # エラー終了
            return   
        # 引数チェック
        if self.val.ValidateInteger(plot, "Argument", 1, len(self.plotList)):
            # 指定されたプロッタが閉じられていなければ
            if self.plotList[plot-1].IsAliveComm():
                # 旧カレントプロッタのアイコン化要求    
                self._IconizeCurrent()
                # カレントプロッタを変更
                self.plot = self.plotList[plot-1]
                # 新カレントプロッタの復元要求    
                self.plot.PutCommand("doiconize 0\n")
            # 既に閉じている場合は
            else:
                # エラーメッセージを表示
                print "\nThe 2D plotter(%d) has been already closed." % plot
     
    ######################################### 
    def _IconizeCurrent(self): 
        """
        カレントプロッタをアイコン化する
        @param  無し
        @retval 無し
        """             
        # 先にカレントプロッタがあれば
        if self.plot != None:
            # カレントプロッタが閉じられていなければ
            if self.plot.IsAliveComm():
                # 旧カレントプロッタのアイコン化要求
                self.plot.PutCommand("doiconize 1\n")

    #########################################         
    def ChangeMap(self, matrix=None, new = False):
        """
        プロッタのデータを変更
        @param  data  変更するデータ
        　　　　エレメントコンテナマトリックス
        @param  new  新規プロッタ
        @retval 無し
        """ 
        # 引数が指定されていない場合
        if not self.val.ValidateObject(matrix, "ChangeData"):
            # エラー終了
            return   

        mapXY = matrix
        
        # テンポラリファイル名称を取得
        temppath = self.tp.GetTempName()
       
        if temppath == None:
            # 異常終了
            print "\nError!! Cannot make a temporary file."   
            return
        
        # メインタイトルとサブタイトルを作る

        #runno, ei = d2.GetHeaderInfo()
        #main = "Run no.: %s" % runno
        main = "Slice S(Q1,Q2,Q3,hw)"
        sub = ""
        
        if new:

            xlabel = "Q(1/Angstrom)"
            ylabel = "Energy(meV)"
            zlabel = "Neutrons"
            
            object = [mapXY, main, sub, "Q(1/Angstrom)", "Energy(meV)","Neutrons"]
            
        
        else:
            object = [mapXY, main, sub]

        # オブジェクトをシリアライズしてテンポラリファイルへ書き込む
        ret = self.tp.MakeTempFile(object, temppath)
        # 正常終了か
        if ret == 0:
            # コマンド作成(改行コード付)
            strCmd = "changedata %s\n" % temppath
            # コマンドの送信要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)
        else:
            # 異常終了
            print "\nError!! Cannot make a temporary file." 


 
        
    #########################################         
    def SetLog(self, flag=True):
        """
        ログモード設定
        @param  flag  ログモード True or False
        @retval 無し
        """ 
        # 引数チェック
        if self.val.ValidateBoolean(flag, "Argument"):
            # コマンド作成
            strCmd = "setlog %d\n" % flag
            # コマンドの送信要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)

    #########################################         
    def SetWithPlot(self, flag=None, n=3):
        """
        プロッタ送信モード設定
        @param  flag  送信モード True or False
        @param   n  1: Xのみ  2: Yのみ 3: 両方
        @retval 無し
        """ 
        # 引数1チェック
        if not self.val.ValidateBoolean(flag, "Argument1"):
            return
        # 引数2 チェック
        if self.val.ValidateInteger(n, "Argument2", 1,3):
            # コマンド作成
            strCmd = "setwith %d %d\n" % (flag,n)
            # コマンドの送信要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)
        
    #########################################         
    def SetZScale(self, z0=None, z1=None ):
        """
        自動スケールモード設定
        @param  z0 X軸始値
        @param  z1 X軸終値
        @retval 無し
        """ 
        # 引数無しか
        if z0==None and z1==None:
            # 自動モード(引数無し)コマンドの送信要求
            self.plot.PutCommand("setzscale\n")
            return
        
        if z1 == None:
            print "\nSetZScale takes none or 2 arguments." 
            return
        
        # 引数1チェック
        if not self.val.ValidateNumeric(z0, "Argument1",0.0):
            # エラー終了
            return 
        # 引数2チェック
        if not self.val.ValidateNumeric(z1, "Argument2" ,0.0):
            # エラー終了
            return 
        # 範囲チェック
        if z1 > z0:   
            # コマンド文字列作成
            strCmd = "setzscale %.3f %.3f\n" % (z0, z1)
            # コマンドの送信要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)
        else:
            print "\nThe argument2 must be larger then the argument1."
                                        
    #########################################         
    def Close(self, plot=None):
        """
        プロッタ画面を閉じる
        @param  pno  プロッタ番号
        @retval 無し
        """ 
        # カレントプロッタのクローズ要求か
        if plot == None:
            self.plot.PutCommand("close\n" )
            
        # 入力チェックがOKか
        if self.val.ValidateInteger (plot, "Argument(Plotter Number)", 0, len(self.plotList)):
            # 全画面クローズ要求か
            if plot == 0:
                # 全プロッタに対し
                for pl in self.plotList:
                    # 生存していれば
                    if pl.IsAliveComm():
                        # クローズ要求を送信
                        pl.PutCommand("close\n" )
            else:
                pnum=int(plot)
                # 指定プロッタのインスタンス取得
                pl = self.plotList[pnum-1]
                # クローズ要求を送信
                pl.PutCommand("close\n" )


    #########################################         
    def SliceX(self, y=None, diff = 0.0, bin = 0.0):
        """
        Y値を指定してX方向にスライスする
        @param  y    Y値
        @param  diff 積算範囲　(Y±diff の範囲で積算)
        @param  bin  リビニング値　(0.0のときリビニングしない)
        @retval 無し
        """ 
        # 引数1チェック
        if y == None:
            print "\nSliceX needs at least one argument."
            return
        if not self.val.ValidateNumeric(y, "Argument1"):
            # エラー終了
            return 
        # 引数2チェック
        if not self.val.ValidateNumeric(diff, "Argument2" , 0.0):
            # エラー終了
            return 
        # 引数3チェック
        if self.val.ValidateNumeric(bin, "Argument3" , 0.0):
            strCmd = "slicex %.3f %.3f %.3f\n" % (y, diff, bin)
            ret = self.plot.PutCommand(strCmd)
    
    #########################################         
    def SliceY(self, x=None, diff = 0.0, bin = 0.0):
        """
        X値を指定してY方向にスライスする
        @param  x    X値
        @param  diff 積算範囲　(X±diff の範囲で積算)
        @param  bin  リビニング値　(0.0のときリビニングしない)
        @retval 無し        """ 
        # 引数1チェック
        if x == None:
            print "\nSliceY needs at least one argument."
            return
        if not self.val.ValidateNumeric(x, "Argument1"):
            # エラー終了
            return 
        # 引数2チェック
        if not self.val.ValidateNumeric(diff, "Argument2" , 0.0):
            # エラー終了
            return 
        # 引数3チェック
        if self.val.ValidateNumeric(bin, "Argument3" , 0.0, 1000.0):
            strCmd = "slicey %.3f %.3f %.3f\n" % (x, diff, bin)
            ret = self.plot.PutCommand(strCmd)
            
    #########################################         
    def SliceXRange(self, y0=None, y1=None, fract=1, bin = 0.0):
        """
        Xデータを切り取るYの範囲を指定        @param y0  X値の始値
        @param  y1  Y値の終値
        @param  fract  分割数
        @param  bin 　ビン値
        @retval 無し        """
        # 引数の有無をチェック
        if y0 == None or y1==None:
            print "\nSliceXRange needs at least two arguments."
            return
        
        # 引数1チェック
        if not self.val.ValidateNumeric(y0, "Argument 1"):
            # エラー終了
            return 
        # 引数2チェック
        if not self.val.ValidateNumeric(y1, "Argument 2" ):
            # エラー終了
            return 

        # 引数3チェック
        if not self.val.ValidateInteger(fract, "Argument 3" , 1,50):
            # エラー終了
            return 
        # 引数4チェック
        if self.val.ValidateNumeric(bin, "Argument 4" , 0.0, 1000.0):
            # 終値 >= 始値チェック
            if y1 > y0:
                strCmd = "slicexrange %.3f %.3f %d %.3f\n" % (y0, y1, fract, bin)
                ret = self.plot.PutCommand(strCmd)
            else:
                print "\nThe argument2 must be larger then the argument1." 
    
    #########################################         
    def SliceYRange(self, x0=None, x1=None, fract=1, bin = 0.0):
        """
        Y データを切り取るXの範囲を指定        @param  x0  X値の始値
        @param  x1  X値の終値
        @param  fract  分割数
        @param  bin 　ビン値
        @retval 無し        """
        # 引数の有無をチェック
        if x0 == None or x1==None:
            print "\nSliceYRange needs at least two arguments."
            return
        # 引数1チェック
        if not self.val.ValidateNumeric(x0, "Argument 1"):
            # エラー終了
            return 
        # 引数2チェック
        if not self.val.ValidateNumeric(x1, "Argument 2" ):
            # エラー終了
            return 

        # 引数3チェック
        if not self.val.ValidateInteger(fract, "Argument 3" , 1,50):
            # エラー終了
            return 
        # 引数4チェック
        if self.val.ValidateNumeric(bin, "Argument 4" , 0.0, 1000.0):
            # 終値 >= 始値チェック
            if x1 > x0:
                strCmd = "sliceyrange %.3f %.3f %d %.3f\n" % (x0, x1, fract, bin)
                ret = self.plot.PutCommand(strCmd)
            else:
                print "\nThe argument2 must be larger then the argument1."  

    #########################################         
    def AddToPlot(self, n=3):
        """
        表示中のデータをプロッタへ送信
        @param  n  1: Xのみ  2: Yのみ 3: 両方
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateInteger(n, "Argument", 1,3):
            strCmd = "add %d\n" % n
            # データ追加要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd) 
        
    #########################################         
    def ClearPlot(self, n=3):
        """
        プロッタをクリア
        @param  n  1: Xのみ  2: Yのみ 3: 両方
        @retval 無
        """
        # 引数チェック
        if self.val.ValidateInteger(n, "Argument", 1,3):
            strCmd = "clear %d\n" % n
            # データ追加要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)
             
    #########################################         
    def SetXLabel(self, xlabel=""):
        """
        X軸のラベル設定
        @param  xlabel　横軸のラベル文字列
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateString(xlabel, "Argument", 0,50):
            # コマンド作成
            strCmd = "xlabel %s\n" % xlabel
            # 座標ラベル変更要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)

    #########################################         
    def SetYLabel(self, ylabel=""):
        """
        Y軸のラベル設定
        @param  xlabel　横軸のラベル文字列
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateString(ylabel, "Argument", 0,50):
            # コマンド作成
            strCmd = "ylabel %s\n" % ylabel
            # 座標ラベル変更要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)

    #########################################         
    def SetZLabel(self, zlabel=""):
        """
        Z軸のラベル設定
        @param  zlabel　横軸のラベル文字列
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateString(zlabel, "Argument", 0,50):
            # コマンド作成
            strCmd = "zlabel %s\n" % zlabel
            # 座標ラベル変更要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)
            
    #########################################         
    def SetMainTitle(self, main=""):
        """
        メインタイトル設定
        @param  main　メインタイトル
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateString(main, "Argument", 0,50):
            # コマンド作成
            strCmd = "maintitle %s\n" % main
            # タイトル変更要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd) 
    
    #########################################         
    def SetSubTitle(self, sub=""):
        """
        サブタイトル設定
        @param  sub　サブタイトル
        @retval 無し
        """
        # 引数チェック
        if self.val.ValidateString(sub, "Argument", 0,50):
            # コマンド作成
            strCmd = "subtitle %s\n" % sub
            # タイトル変更要求をカレントプロッタへ送る
            self.plot.PutCommand(strCmd)               
                                     
    #########################################         
    def Print(self):
        """
        カレントプロッタを印刷する
        @param  無し
        @retval 無し
        """ 
        # 印刷要求をカレントプロッタへ送る
        answer = self.plot.PutCommand("print\n" ) 
        
    #########################################         
    def Save(self, fname=None):
        """
        画像ファイルの保存
        @param  ファイル名
        @retval 無し
        """
        # 引数が指定されていない場合
        if not self.val.ValidateObject(fname, "Save"):
            # エラー終了
            return   
        # 引数チェック
        if not self.val.ValidateString(fname, "Argument", 4,256):
            return
        
        # ファイル名取り出し
        filename = os.path.basename(fname)
        path, ext = os.path.splitext(filename)
        # 拡張子を除くファイル名の長さは1以上か
        if len(path) > 1:
            # サポートしている拡張子か
            if ext==".png" or ext==".ps" or ext==".eps" :
                strCmd = "save " + fname + '\n'
                # 画像保存要求をカレントプロッタへ送る
                self.plot.PutCommand(strCmd) 
            else:
                print "\nError!!  Extention must be png, ps, or eps."
        else:
            print "\nError!! Invalid file name."

#####################################################################
if __name__ == '__main__':
    """
    以下はテスト用のスクリプト

    """
    ##[Inamura 121228: delete test script]
