# -*- coding: utf-8 -*-
#
# This file is part of couchapp released under the Apache 2 license. 
# See the NOTICE for more information.

import glob
from hashlib import md5
import logging
import os
import re

from couchapp.errors import MacroError
from couchapp import util

logger = logging.getLogger(__name__)


def package_shows(doc, funcs, app_dir, objs):
   apply_lib(doc, funcs, app_dir, objs)
         
def package_views(doc, views, app_dir, objs):
   for view, funcs in views.iteritems():
       if hasattr(funcs, "items"):
           apply_lib(doc, funcs, app_dir, objs)

def apply_lib(doc, funcs, app_dir, objs):
    for k, v in funcs.items():
        if not isinstance(v, basestring):
            continue
        else:
            logger.debug("process function: %s" % k)
            old_v = v
            try:
                funcs[k] = run_json_macros(doc, 
                    run_code_macros(v, app_dir), app_dir)
            except ValueError, e:
                raise MacroError(
                "Error running !code or !json on function \"%s\": %s" % (k, e))
            if old_v != funcs[k]:
                objs[md5(util.to_bytestring(funcs[k])).hexdigest()] = old_v
           
def run_code_macros(f_string, app_dir):
   def rreq(mo):
       # just read the file and return it
       path = os.path.join(app_dir, mo.group(2).strip())
       library = ''
       filenum = 0
       for filename in glob.iglob(path):            
           logger.debug("process code macro: %s" % filename)
           try:
               cnt = util.read(filename)
               if cnt.find("!code") >= 0:
                   cnt = run_code_macros(cnt, app_dir)
               library += cnt
           except IOError, e:
               raise MacroError(str(e))
           filenum += 1
           
       if not filenum:
           raise MacroError(
           "Processing code: No file matching '%s'" % mo.group(2))
       return library

   re_code = re.compile('(\/\/|#)\ ?!code (.*)')
   return re_code.sub(rreq, f_string)

def run_json_macros(doc, f_string, app_dir):
   included = {}
   varstrings = []

   def rjson(mo):
       if mo.group(2).startswith('_attachments'): 
           # someone  want to include from attachments
           path = os.path.join(app_dir, mo.group(2).strip())
           filenum = 0
           for filename in glob.iglob(path):
               logger.debug("process json macro: %s" % filename)
               library = ''
               try:
                   if filename.endswith('.json'):
                       library = util.read_json(filename)
                   else:
                       library = util.read(filename)
               except IOError, e:
                   raise MacroError(str(e))
               filenum += 1
               current_file = filename.split(app_dir)[1]
               fields = current_file.split('/')
               count = len(fields)
               include_to = included
               for i, field in enumerate(fields):
                   if i+1 < count:
                       include_to[field] = {}
                       include_to = include_to[field]
                   else:
                       include_to[field] = library
           if not filenum:
               raise MacroError(
               "Processing code: No file matching '%s'" % mo.group(2))
       else:	
           logger.debug("process json macro: %s" % mo.group(2))
           fields = mo.group(2).strip().split('.')
           library = doc
           count = len(fields)
           include_to = included
           for i, field in enumerate(fields):
               if not field in library:
                   logger.warning(
                   "process json macro: unknown json source: %s" % mo.group(2))
                   break
               library = library[field]
               if i+1 < count:
                   include_to[field] = include_to.get(field, {})
                   include_to = include_to[field]
               else:
                   include_to[field] = library

       return f_string

   def rjson2(mo):
       return '\n'.join(varstrings)

   re_json = re.compile('(\/\/|#)\ ?!json (.*)')
   re_json.sub(rjson, f_string)

   if not included:
       return f_string

   for k, v in included.iteritems():
       varstrings.append("var %s = %s;" % (k, util.json.dumps(v).encode('utf-8')))

   return re_json.sub(rjson2, f_string)
