# -*- coding: utf-8 -*-
"""
UtilQt.py
ユーティリティモジュール

History by Y.Inamura
[inamura 170126] changed to send "map-format" data to M2Plot (for VisualContQ)
                 - add a new argument about file name extention to TempFile::GetTempName
[inamura 120512] add marker face status "fill" or "none"
[inamura 110303] add header info for CalcDspacing.
[inamura 110223] save and load default attribute value of FastPlot
"""
from __future__ import print_function
import time
import codecs
import sys
from xml.dom.minidom import parse
import tempfile
import os.path
import os
import platform
if platform.python_version_tuple()[0] == '2':
    import cPickle as pickle
else:
    import pickle


try:
    import Manyo
except:
    pass

try:
    from PySide6 import QtGui, QtWidgets
except:
    try:
        from PySide2 import QtGui, QtWidgets
    except:
        from PySide import QtGui
        import PySide.QtGui as QtWidgets
# リソースファイル名称定義
RESOURCEFILE = "FastVisualize"

# メッセージファイル名称定義
MESSAGEFILE = "Message.xml"
# マスク値定義
# MASKVALUE = 100000000.0
try:
    import Manyo.Utsusemi as mu
    MASKVALUE = mu.UTSUSEMIMASKVALUE64
except:
    MASKVALUE = 1.0E15

#######################################
#  IFEvtProp
#######################################


class IFEvtProp(object):
    """
    イベントとプロパティ授受用のインターフェイスクラス
    """

    dicInstance = {}

    #########################################
    def __new__(cls, idno=0):
        """
        idno 毎に異なるインスタンスを返すコンストラクタ
        (同一グループ間では共通のインスタンスを使用)
        @param  cls 　クラス
        @param  idno  グループID
          　　　　　　プロッタ番号をIDとして使用
        @retval クラスのインスタンス
        """

        # 指定されたID のクラスインスタンスが生成済みなら
        if idno in cls.dicInstance:
            # 生成済みのインスタンスを返す
            instance = cls.dicInstance[idno]
        else:
            # クラスインスタンスが未生成なら新規に構築
            instance = object.__new__(cls)

        return instance

    #########################################
    def __init__(self, idno=0):
        """
        インターフェイスクラスのコンストラクタ
        @param  idno  グループID
        @retval クラスのインスタンス
        """
        # クラスインスタンスが登録済みなら
        if idno in self.dicInstance:
            # 何もしない
            return

        # クラスインスタンスを登録
        self.dicInstance[idno] = self

        # 登録リストを初期化
        self.InitIF()

    #########################################

    def InitIF(self):
        """
        リスナーリストとプロパティディクショナリの初期化
        @param  無し
        @retval 無し
        """
        # リスナーリストの初期化
        self.listner = []
        # プロパティディクショナリの初期化
        self.property = {}

    #########################################
    def AddListner(self, evtType, func):
        """
        イベントに対するリスナー登録追加
        @param  evtType  イベントタイプ(文字列)
        @param  func     イベント発生時に実行する関数
        @retval 無し
        """
        # イベント発生時に実行する関数を登録
        self.listner.append((evtType, func))

    #########################################
    def AddProperty(self, propKey, func):
        """
        公開するプロパティを登録
        @param  propKey  属性名称(文字列)
        @param  func     プロパティが要求されたときに実行する関数(ラムダ式)
        @retval 無し
        """
        # プロパティを返すラムダ式を登録
        self.property[propKey] = func

    #########################################
    def NotifyEvent(self, wid, evtType, value=None):
        """
        イベントに登録されている関数を実行
        (同一イベントに複数登録されている場合は、順番にすべて実行)
        @param  wid      イベント発生元のインスタンス
        @param  evtType  発生したイベントのタイプ(文字列)
        @param  value    イベントの値(省略可)
        @retval 無し
        """
        # リスナーリストから、イベントに登録されているものを探す
        for listner in self.listner:
            evt, func = listner
            # イベントタイプが一致したなら
            if evt == evtType:
                # イベントに登録されている関数を全て実行
                func(*(wid, evtType, value))

    #########################################
    def GetProperty(self, propKey):
        """
        プロパティ取得
        @param  propKey  属性名称(文字列)
        @retval プロパティ
        """
        retval = None
        # プロパティのkey から lambda 式を取得
        try:
            try:
                func = self.property[propKey]
            except:
                raise PlotException(
                    'Common', 'C001', ("Key error:" + propKey, "IFEvtProp:GetProperty"))
            else:
                # プロパティを取得(lambda 式実行)
                try:
                    retval = func()
                except:
                    raise PlotException(
                        'Common', 'C001', ("Lambda execution", "IFEvtProp:GetProperty"))
        except PlotException as ex:
            PlotMessage(None, ex)

        return retval


#######################################
#  MakeTempFile
#######################################
class TempFile(object):
    """
    テンポラリファイルクラス
    """
    ###################################################

    def GetTempName(self, ext=""):  # [inamura 170126]
        """
        テンポラリファイルの名称を取得
        (モジュール tempfile を使用してファイル名を取得すると、
        消せなくなるため、自分でファイル名を作成)
        @param  無し
        @retval テンポラリファイル名称(フルパス)
                 名称取得に失敗した場合は、None を返す
        """
        # 現在の絶対時間(0.01秒単位)と、プロッタ番号よりテンポラリファイル名を作成
        tsec = int(time.time() * 100.0)
        # tmpf = "tmp%d" % tsec
        tmpf = "tmp%d%s" % (tsec, ext)  # [inamura 170126]
        # テンポラリファイルの絶対パスを作成(指定されたテンポラリディレクトリの下)
        tmppath = os.path.join(tempfile.gettempdir(), tmpf)

        # テンポラリファイルの存在チェック
        for i in range(100):
            # 万一同じファイル名があったら、ファイル名を変更
            if os.path.exists(tmppath):
                # tmppath = "%s%d" % (tmppath, i)
                tmppath = "%s%d%s" % (tmppath, i, ext)  # [inamura 170126]
            else:
                # 無ければOK
                break
            if i == 99:
                return None

        return tmppath

    ###################################################
    def MakeTempFile(self, object, temppath):
        """
        Python オブジェクトをシリアライズして
        テンポラリファイルに書き込む
        @param  object    シリアライズするオブジェクト
        @param  temppath  シリアライズしたオブジェクトを書きこむファイル
        @retval  0: 成功、1: テンポラリファイルオープン失敗、2: シリアライズ失敗
        """
        try:
            # テンポラリファイルを開く
            fw = open(temppath, "wb")
        except:
            return 1
        try:
            # オブジェクトをバイナリモードでシリアライズ
            pickle.dump(object, fw, True)
        except:
            # テンポラリファイルをクローズ
            fw.close()
            return 2
        # テンポラリファイルをクローズ
        fw.close()
        # 正常終了
        return 0

    ###################################################
    def ReadTempFile(self, temppath):
        """
        テンポラリファイルを読み込み、デシリアライズかる。
        @param  temppath  シリアライズしたオブジェクトを書きこんだファイル
        @retval  デシリアライズしたデータ、デシリアライズ失敗時は None
        """
        try:
            # テンポラリファイルを読み込む
            fr = open(temppath, "rb")
            # データを復元(デシリアライズ)
            data = pickle.load(fr)
        except:
            return None
        # テンポラリファイルをクローズ
        fr.close()
        # テンポラリファイルを削除
        os.remove(temppath)
        # デシリアライズしたオブジェクトを返す
        return data

#######################################
#  AcqPath
#######################################


class AcqPath(object):
    """
    ディレクトリパス取得ユーティリティ
    """
    ###########################################################################

    def GetHome(self):
        """
        ユーザホームディレクトリ取得
        @param    無し
        @retval   ディレクトリのパス (string)
        """
        # ユーザのホームディレクトリを取得
        try:
            home = os.environ['UTSUSEMI_USR_DIR']
        except:
            # Windows 上で環境変数を設定していない場合はカレントディレクトリとする
            home = os.getcwd()

        return home

    ###########################################################################
    def GetHomeExp(self):
        """
        ユーザホームディレクトリ下の exp パスを取得
        @param    無し
        @retval   ディレクトリのパス (string)
        """
        # ユーザのホームディレクトリを取得
        home = self.GetHome()
        return os.path.join(home, 'exp')

    ###########################################################################
    def GetPrmPath(self):
        """
        ユーザパラメータのパスを取得
        格納場所はホームディレクトリ下の　exp/xml
        @param    無し
        @retval   パラメータディレクトリのパス
        """
        # ホーム下のexpを取得
        exp = self.GetHomeExp()
        return os.path.join(exp, 'xml')

    ###########################################################################
    def GetCurrentMth(self):
        """
        カレントメソッドのパスを取得
        @param    無し
        @retval   カレントファイルのパス (string)
        """
        # ユーザのホームディレクトリを取得
        exp = self.GetHomeExp()
        return os.path.join(exp, CURRENT_FILE)

    ###########################################################################
    def GetSysExp(self):
        """
        システムディレクトリ下のexpパスを取得
        格納場所はファイルサーバ
        @param    無し
        @retval   システムディレクトリ下のexpパス
        """
        # システムディレクトリを取得
        try:
            sysd = os.environ["UTSUSEMI_BASE_DIR"]
        except:
            if os.name == "nt":
                sysd = 'c:\\mlf'
            else:
                sysd = '/usr/local/mlf'

        return os.path.join(sysd, 'exp')

    ###########################################################################
    def GetSysAnal(self):
        """
        システムディレクトリ下のanalパスを取得
        格納場所はファイルサーバ
        @param    無し
        @retval   システムディレクトリ下の anal パス
        """
        # システムディレクトリを取得
        try:
            sysd = os.environ["UTSUSEMI_BASE_DIR"]
        except:
            if os.name == "nt":
                sysd = 'c:\\mlf'

            else:
                sysd = '/usr/local/mlf'

        return os.path.join(sysd, 'ana')

    ###########################################################################
    def GetSysVis(self):
        """
        システムディレクトリ下のvisパスを取得
        格納場所はファイルサーバ
        @param    無し
        @retval   システムディレクトリ下の vis パス
        """
        # システムディレクトリを取得
        try:
            sysd = os.environ["UTSUSEMI_BASE_DIR"]
        except:
            if os.name == "nt":
                sysd = 'c:\\mlf'

            else:
                sysd = '/usr/local/mlf'

        return os.path.join(sysd, 'vis')

    ###########################################################################
    def GetDefaultMth(self):
        """
        デフォルトメソッドファイルのパス取得
        ベースディレクトリ下の　exp/xml
        @param    無し
        @retval   ディレクトリのパス (string)
        """
        # ベースディレクトリを取得
        base = os.environ["UTSUSEMI_BASE_DIR"]
        if base is None:
            if os.name == "nt":
                base = 'c:\\mlf'
            else:
                base = '/usr/local/mlf'

        sysName = os.environ["UTSUSEMI_INST_CODE"]
        if sysName is None:
            sysName = "AMR"

        path = os.path.join(base, sysName)
        path = os.path.join(path, 'exp')
        path = os.path.join(path, 'xml')
        return os.path.join(path, DEFAULT_FILE)

    ###########################################################################
    def GetPmtPath(self, flag):
        """
        PMTファイルのパス取得
        ベースディレクトリ下の　ana/xml
        @param    flag  測定フラグ
        @retval   ディレクトリのパス (string)
        """
        # ベースディレクトリを取得
        try:
            # base = os.environ["UTSUSEMI_USR_DIR"]  ##[okazaki, inamura 140310]
            base = os.environ.get("UTSUSEMI_USR_DIR", None) \
                or mu.UtsusemiGetInstDir()
            #       or os.enviton["UTSUSEMI_USR_DIR"]
        except:
            if os.name == "nt":
                base = 'c:\\mlf'
            else:
                base = '/usr/local/mlf'
        if flag:
            path = os.path.join(base, 'exp')
        else:
            path = os.path.join(base, 'ana')

        # ディレクトリがあるか
        # [okazaki, inamura 140310]-->
        # if not os.path.isdir(path):
        #    os.mkdir(path)
        # PMT用ディレクトリがあるか
        # ppath = os.path.join(path,'pmt')
        # if not os.path.isdir(ppath):
        #    os.mkdir(ppath)
        # return ppath
        try:
            if not os.path.isdir(path):
                os.mkdir(path)
            # PMT用ディレクトリがあるか
            pmtpath = os.path.join(path, 'pmt')
            if not os.path.isdir(pmtpath):
                os.mkdir(pmtpath)
        except:
            # pmtpath = os.environ["HOME"]
            pmtpath = os.getcwd()
        return pmtpath
        # <--[okazaki, inamura 140310]

    #########################################

    def GetModulePath(self, module):
        """
        モジュールパスの探索
        @param  module  　ファイル名
        @retval モジュールのフルパス
        """
        # カレントディレクトリを取得
        path = os.getcwd()
        fpath = os.path.join(path, module)
        # カレントディレクトリに目的ファイルがあるか
        if os.path.isfile(fpath):
            # フルパスを返す
            return fpath

        # 環境変数に指定されたディレクトリを取得
        paths = [self.GetSysExp(), self.GetSysAnal(), self.GetSysVis()]
        # 全PYTHONPATHディレクトリを探索
        for path in paths:
            fpath = os.path.normpath(os.path.join(path, module))
            # ディレクトリに目的ファイルがあるか
            if os.path.isfile(fpath):
                # フルパスを返す
                return fpath

        # 目的ファイルが見つからなかった
        return None


#######################################
#  PlotException
#######################################
class PlotException(Exception):
    """
    例外処理用コンテナクラス
    測定条件決定支援処理プログラム内で例外オブジェクトとして使用する
    メッセージ及びエラーコードは当クラスから取得できる
    """

    def __init__(self, parent, key, tap):
        """
        コンストラクタ
        @param parent:メッセージのカテゴリ
        @param key:メッセージID
        @param tap:編集パラメータ(tupleオブジェクト）

        """
        self.parent = parent
        self.key = key
        self.tap = tap

    ################################
    def GetMessage(self):
        """
        メッセージの取得
        @params 無し
        @retval msg:メッセージ
        """
        msg = MessageFile()
        return msg.GetMessage(self.parent, self.key, self.tap)

    ################################
    def GetErrorCode(self):
        """
        エラーコードの取得
        @param 無し
        @retval code:エラーコード
        """
        return self.code

    ################################
    def GetDetailCode(self):
        """
        詳細コードの取得
        @param 無し
        @retval detail:詳細コード
        """
        return self.detail

#######################################
#  MessageFile
#######################################


class MessageFile(object):
    """
    MessageFileクラス
    MessageFileの読み込みとMessageの編集を行う
    Singletonクラスとして利用
    """
    instance = None
    lang = 'English'

    ################################
    def __new__(cls, *args):
        """
        Singletonコンストラクタ
        @param 無し
        @retval　無し
        """
        if cls.instance is None:
            cls.instance = object.__new__(cls, *args)
            # メッセージファイル読み込み
            cls.instance.Load()
        return cls.instance

    ################################
    def Load(self):
        """
        MessageFileの読み込み
        @param 　無し
        @retval 無し
        """
        # メッセージファイルの絶対パスを作る
        msgpath = self.SearchPath(MESSAGEFILE)
        try:
            # メッセージファイルをXML パーサに読み込み
            self.xmlDoc = parse(msgpath)
            self.isRead = True
        except:
            self.isRead = False

    ################################
    def GetMessage(self, parent, key, tap):
        """
        編集されたMessageの取得
        @param parent:メッセージのカテゴリ
        @param key:メッセージID
        @param tap:編集パラメータ(tupleオブジェクト）
        @retval Message
        """
        # --> 2008/04/28 Minakawa
        # メッセージファイルを読み込み済みか
        if not self.isRead:
            self.Load()
        # <-- 2008/04/28 Minakawa

        # メッセージファイルの読み込みに成功しているか
        if self.isRead:
            # 親ノードを取得
            node = self.xmlDoc.getElementsByTagName(parent)[0]
            # 言語ノードを取得
            lang = node.getElementsByTagName(self.lang)[0]
            try:
                # メッセージ文字列を取得
                child = lang.getElementsByTagName(key)[0]
            except TypeError:
                raise PlotException('Common', 'C001', (key, "Wrong Code"))
            try:
                # 文字列にタプルで指定された引数を入れこむ
                return child.firstChild.data % (tap)
            except TypeError:
                raise PlotException('Common', 'C001', (key, "TypeError"))
        else:
            return 'Message File Read Error!! ( Message.GetMessage() )'

    #########################################
    def SearchPath(self, fname):
        """
        フルパスの探索
        @param  fname  　ファイル名
        @retval ファイルのフルパス
        """
        # カレントディレクトリを取得
        self.filePath = os.getcwd()
        fpath = os.path.join(self.filePath, fname)
        # カレントディレクトリに目的ファイルがあるか
        if os.path.isfile(fpath):
            # フルパスを返す
            return fpath
        self.filePath = ""
        # 環境変数 UTSUSEMI_BASE_DIR に指定されたディレクトリを取得
        try:
            path = os.environ["UTSUSEMI_BASE_DIR"]
            path = os.path.join(path, "vis")
            fpath = os.path.join(path, fname)
            if os.path.isfile(fpath):
                # フルパスを返す
                return fpath

        except:
            return ""

        # [inamura 100720]-->
        # for StandAlone
        if path is None:
            p_path = os.environ.get('PYTHONPATH')
            if p_path is not None:
                p_path_list = p_path.split(os.pathsep)
                path_list = []
                for pp in p_path_list:
                    path_list.append(pp)
                    path_list.append(os.path.join(pp, "vis"))
                for pp in path_list:
                    fp = os.path.normpath(os.path.join(pp, fname))
                    if os.path.isfile(fp):
                        self.filePath = pp
                        return fp
        else:
            return ""

        """
        path = os.path.join(path,'vis')
        fpath = os.path.join(path,fname)

        # ディレクトリに目的ファイルがあるか
        if os.path.isfile(fpath):
            # ディレクトリを設定
            self.filePath = path
            # フルパスを返す
            return fpath
        else:
            return ""
        """
        # <--[inamura 100720]

        """
        # 環境変数PYTHONPATHに指定されたディレクトリを取得
        paths = str( os.environ.get('PYTHONPATH')).split(os.pathsep)
        # 全PYTHONPATHディレクトリを探索
        for path in paths:
            fpath = os.path.normpath(os.path.join(path,fname))
            # ディレクトリに目的ファイルがあるか
            if os.path.isfile(fpath):
                # ディレクトリを設定
                self.filePath = path
                # フルパスを返す
                return fpath

        # 目的ファイルが見つからなかった
        return ""
        """

    ################################
    def CloseDoc(self):
        """
        XMLドキュメントの解放
        @param  無
        @retval 無
        """
        if self.isRead:
            self.xmlDoc.unlink()
            self.isRead = False   # 208/04/28 Minakawa


#######################################
#  PlotMessage
#######################################
class PlotMessage(object):
    """
    各種メッセージの表示クラス
    有効なウィンドウが指定されない場合はコンソールへのテキスト出力
    親ウィンドウが指定されていれば、MessageBoxを表示
    """

    def __init__(self, parent, ex):
        """
        コンストラクタ
        @param parent:親ウィンドウオブジェクト（無ければNoneを指定）
        @param ex:PlotExceptionのインスタンス
        @retval 無し
        """
        msg = ex.GetMessage()

        # MessageBoxを表示する親ウィンドウが無効であればコンソール出力

        if parent is None:
            # traceback.print_stack()
            sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)
            print(msg)
        else:
            # wx.MessageBox(msg,caption='Message',style=wx.OK)
            ret = QtWidgets.QMessageBox().information(
                None, u"Warning", msg, QtWidgets.QMessageBox.Ok)


#######################################
#  ImagesXpm
#######################################
class ImagesXpm(object):
    """
    Image data for QT
    """
    instance = None

    def __new__(cls, *args):
        """
        Singletonコンストラクタ
        @param  *args 可変引数(無し)
        @retval クラスのインスタンス
        """
        if cls.instance is None:
            # クラスインスタンスが未生成なら新規に構築
            cls.instance = object.__new__(cls, *args)
        return cls.instance

    #########################################
    def GetQPixmap(self, iname):
        if iname in self.imageData:
            return QtGui.QPixmap(self.imageData[iname])
        else:
            return None

    imageData = {
        "Green": ["20 20 3 1 ",
                  "  c black", ". c green", "X c white",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXX   XXXXXXXXXX",
                  "XXXXX  ...  XXXXXXXX",
                  "XXXX ..   .. XXXXXXX",
                  "XXX .  XXX  . XXXXXX",
                  "XXX . XXXXX . XXXXXX",
                  "XX . XXXXXXX . XXXXX",
                  "XX . XXXXXXX . XXXXX",
                  "XXX . XXXXX . XXXXXX",
                  "XXX .  XXX  . XXXXXX",
                  "XXXX ..   .. XXXXXXX",
                  "XXXXX  ...  XXXXXXXX",
                  "XXXXXXX   XXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX",
                  "XXXXXXXXXXXXXXXXXXXX"],
        "Red": ["20 20 3 1 ",
                "  c black", ". c red", "X c white",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXX   XXXXXXXXXX",
                "XXXXX  ...  XXXXXXXX",
                "XXXX ..   .. XXXXXXX",
                "XXX .  XXX  . XXXXXX",
                "XXX . XXXXX . XXXXXX",
                "XX . XXXXXXX . XXXXX",
                "XX . XXXXXXX . XXXXX",
                "XXX . XXXXX . XXXXXX",
                "XXX .  XXX  . XXXXXX",
                "XXXX ..   .. XXXXXXX",
                "XXXXX  ...  XXXXXXXX",
                "XXXXXXX   XXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX",
                "XXXXXXXXXXXXXXXXXXXX"],
        'Processing': ["20 20 4 1 ",
                       "  c black", ". c green", "X c yellow", "o c white",
                       "oooooooooooooooooooo",
                       "oooooooooooooooooooo",
                       "oooooooo ooooooooooo",
                       "oooooooooooooooooooo",
                       "oo oooo   oooo ooooo",
                       "ooooo  .X.  oooooooo",
                       "oooo .X   X. ooooooo",
                       "ooo X  XXX  X oooooo",
                       "ooo . XXXXX . oooooo",
                       "oo X XXXXXXX X o ooo",
                       " o . XXXXXXX . ooooo",
                       "ooo X XXXXX X oooooo",
                       "ooo .  XXX  . oooooo",
                       "oooo X.   .X ooooooo",
                       "ooooo  X.X  oooooooo",
                       "oo oooo   oooo ooooo",
                       "oooooooooooooooooooo",
                       "oooooooo ooooooooooo",
                       "oooooooooooooooooooo",
                       "oooooooooooooooooooo"],
        'GreenEnd': ["20 20 3 1 ",
                     "  c black", ". c green", "X c white",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXX   XXXXXXXXXX",
                     "XXXXX  ...  XXXXXXXX",
                     "XXXX ..   .. XXXXXXX",
                     "XXX .  ...  . XXXXXX",
                     "XXX . ..... . XXXXXX",
                     "XX . ....... . XXXXX",
                     "XX . ....... . XXXXX",
                     "XXX . ..... . XXXXXX",
                     "XXX .  ...  . XXXXXX",
                     "XXXX ..   .. XXXXXXX",
                     "XXXXX  ...  XXXXXXXX",
                     "XXXXXXX   XXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX",
                     "XXXXXXXXXXXXXXXXXXXX"],
        'RedEnd': ["20 20 4 1 ",
                   "  c black", ". c red", "X c green", "o c white",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo",
                   "ooooooo   oooooooooo",
                   "ooooo  XXX  oooooooo",
                   "oooo XX   XX ooooooo",
                   "ooo X  ...  X oooooo",
                   "ooo X ..... X oooooo",
                   "oo X ....... X ooooo",
                   "oo X ....... X ooooo",
                   "ooo X ..... X oooooo",
                   "ooo X  ...  X oooooo",
                   "oooo XX   XX ooooooo",
                   "ooooo  XXX  oooooooo",
                   "ooooooo   oooooooooo",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo",
                   "oooooooooooooooooooo"]
    }


###############################################################################
# テスト用スクリプト
###############################################################################
if __name__ == '__main__':

    pass
