#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui Core.
#
# Copyright (C) 2009-2012 HDE, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

import os
import re
import sys
import time

from karesansui.lib.file.configfile import ConfigFile
from karesansui.lib.dict_op import DictOp
from karesansui.lib.utils import preprint_r


"""
コメントを扱うパーサ
有効な設定行の間のコメントは、次の有効な設定のコメントとみなす

---------------------------------
# comment-1
# comment-2

foo = bar1 bar2  # comment-3
---------------------------------
上記のようなファイルの場合、配列は以下のようになります

value["foo"]["value"] =
  [
    "bar1 bar2",
    ["# comment-1","#comment-2"],
    "# comment-3",
  ]
"""
class commentDealParser:

    _delim               = "[ \t]+"
    _new_delim           = " "
    _comment             = "#"
    _reserved_key_prefix = "@"

    _module  = "comment_deal_parser"
    _footer  = "-- Generated by karesansui"

    def __init__(self,paths=[]):
        self.dop = DictOp()
        self.dop.addconf(self._module,{})
        self.set_source_file(paths)

    def set_delim(self, delim=" "):
        self._delim = delim

    def set_new_delim(self, delim=" "):
        self._new_delim = delim

    def set_comment(self, comment="#"):
        self._comment = comment

    def set_reserved_key_prefix(self, prefix="@"):
        self._reserved_key_prefix = prefix

    def set_footer(self, footer=""):
        self._footer = footer

    def set_source_file(self,paths=[]):
        if type(paths) == str:
            paths = [paths]
        self.paths = paths
        return True

    def get_source_file(self):
        return self.paths

    def source_file(self):
        return self.get_source_file()

    def build_value(self, string, precomment=[], postcomment=None):
        if type(precomment) == str:
            precomment = [precomment]
        return [string, [precomment, postcomment], ]

    def read_conf(self):
        retval = {}

        for _afile in self.source_file():
            res = ConfigFile(_afile).read()

            orders    = []
            comment_1 = []   # 設定の前のコメント リスト配列
            comment_2 = None # 設定行のコメント 文字列
            for _aline in res:
                _aline = _aline.rstrip('\r\n')

                if _aline.strip() == "":
                    comment_1.append(_aline)
                    continue

                if _aline.lstrip()[0:1] == self._comment:
                    footer_regex = re.compile(self._footer)
                    m = footer_regex.search(_aline)
                    if not m:
                        comment = _aline[_aline.rfind(self._comment):]
                        comment_1.append(comment)
                        continue

                regex_str = "^(?P<key>[^ \t]+)%s(?P<value>.*)$" % (self._delim,)
                regex = re.compile(r"%s" % regex_str)

                m = regex.match(_aline)
                if m:
                    key     = m.group('key')
                    value   = m.group('value')
                    if not value.rfind(self._comment) == -1:
                        comment_2 = value[value.rfind(self._comment):]
                        value = value[:value.rfind(self._comment)]

                    new_value = self.build_value(value,comment_1,comment_2)
                    if new_value is not False:
                        self.dop.set(self._module,[_afile,key],new_value)
                        orders.append(key)
                    comment_1 = []
                    comment_2 = None

            if len(comment_1) > 0:
                eof_key    = "%sEOF"    % (self._reserved_key_prefix,)
                new_value = self.build_value("",comment_1,comment_2)
                self.dop.set(self._module,[_afile,eof_key],new_value)

            orders_key = "%sORDERS" % (self._reserved_key_prefix,)
            self.dop.set(self._module,[_afile,orders_key],orders)

        #self.dop.preprint_r(self._module)
        return self.dop.getconf(self._module)


    def _value_to_lines(self,value):
        lines = []

        for _k,_v in value.iteritems():

            try:
                if _v['action'] == "delete":
                    continue
            except:
                pass

            iscomment = False
            try:
                iscomment = _v['comment']
            except:
                pass

            _prefix = ""
            if iscomment is True:
                _prefix += self._comment

            try:
                val = _v['value'][0]

                comment_1 = []
                try:
                    for com1_aline in _v['value'][1][0]:
                        if com1_aline.strip() == "":
                            pass
                        elif com1_aline[0:1] != self._comment:
                            com1_aline = "%s %s" % (self._comment,com1_aline,)
                        comment_1.append(com1_aline)
                except:
                    pass

                comment_2 = _v['value'][1][1]
                try:
                    if comment_2[0:1] != self._comment:
                        comment_2 = "%s %s" % (self._comment,comment_2,)
                except:
                    pass

                lines = lines + comment_1
                aline = "%s%s%s%s" % (_prefix,_k,self._new_delim,val,)
                if comment_2 is not None:
                    aline = "%s %s" % (aline,comment_2,)
                lines.append(aline)
            except:
                pass

        return lines


    def write_conf(self,conf_arr={},dryrun=False):
        retval = True

        self.dop.addconf(self._module,conf_arr)
        orders_key = "%sORDERS" % (self._reserved_key_prefix,)
        eof_key    = "%sEOF"    % (self._reserved_key_prefix,)

        for _path,_v in conf_arr.iteritems():

            if _path[0:1] != "/":
                continue

            lines = []
            try:
                _v['value']
            except:
                continue

            exclude_regex = "^%s[A-Z0-9\_]+$" % self._reserved_key_prefix

            # まずはオーダの順
            if self.dop.isset(self._module,[_path,orders_key]) is True:
                for _k2 in self.dop.get(self._module,[_path,orders_key]):
                    m = re.match(exclude_regex,_k2)
                    if not m:
                        try:
                            if type(_k2) == list:
                                _k2 = _k2.pop()
                            value = {}
                            value[_k2] = _v['value'][_k2]
                            lines = lines + self._value_to_lines(value)
                            self.dop.unset(self._module,[_path,_k2])
                        except:
                            pass

            # オーダにないものは最後に追加
            for _k2,_v2 in self.dop.get(self._module,[_path]).iteritems():
                #if _k2 != orders_key and _k2 != eof_key:
                m = re.match(exclude_regex,_k2)
                if not m:
                    try:
                        value = {}
                        value[_k2] = _v2
                        lines = lines + self._value_to_lines(value)
                    except:
                        pass

            # 最後のコメント用の処理
            if self._footer != "":
                if self.dop.isset(self._module,[_path,eof_key]) is False:
                    self.dop.cdp_set(self._module,[_path,eof_key],"",force=True)

                eof_val     = self.dop.get(self._module,[_path,eof_key])
                eof_action  = self.dop.action(self._module,[_path,eof_key])
                eof_comment = self.dop.comment(self._module,[_path,eof_key])
                try:
                    key = " %s - %s on %s" % (self._footer,self._module,time.strftime("%c",time.localtime()))
                    value = {}
                    value[key] = {}
                    value[key]["value"]   = eof_val
                    value[key]["action"]  = eof_action
                    value[key]["comment"] = eof_comment
                    self.set_new_delim(delim=" ")
                    lines = lines + self._value_to_lines(value)
                except:
                    pass

            if dryrun is False:
                if len(lines) > 0:
                    ConfigFile(_path).write("\n".join(lines) + "\n")
            else:
                #pass
                print "\n".join(lines)

        return retval

