# Copyright (C) 2013 Google Inc., authors, and contributors <see AUTHORS file>
# Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
# Created By: dan@reciprocitylabs.com
# Maintained By: dan@reciprocitylabs.com

from .base import *
from ggrc.models import Directive, Control, System, Process, Program, DirectiveControl, ProgramControl
from .base_row import *
from collections import OrderedDict
from ggrc.models.control import CATEGORY_CONTROL_TYPE_ID, CATEGORY_ASSERTION_TYPE_ID

class ControlRowConverter(BaseRowConverter):
  model_class = Control

  def find_by_slug(self, slug):
    from sqlalchemy import orm
    return self.model_class.query.filter_by(slug=slug).options(
        orm.joinedload('directive_controls')).first()

  def setup_object(self):
    self.obj = self.setup_object_by_slug(self.attrs)
    if self.obj.id is not None:
      self.add_warning('slug', "Control already exists and will be updated")

  def reify(self):
    self.handle('slug', SlugColumnHandler)
    self.handle_date('start_date')
    self.handle_date('end_date')
    self.handle_date('created_at', no_import=True)
    self.handle_date('updated_at', no_import=True)
    self.handle_text_or_html('description')

    self.handle_raw_attr('title', is_required=True)
    self.handle_raw_attr('url')

    self.handle_option('kind', role='control_kind')
    self.handle_option('means', role='control_means')
    self.handle_option('verify_frequency')

    self.handle_boolean('key_control', truthy_values=['key', 'key_control',
                        'key control'], no_values=[])
    fraud_truthy = ['fraud', 'fraud_related', 'fraud related']
    fraud_falses = ['not fraud', 'not_fraud','not fraud_related', 'not fraud related']
    self.handle_boolean('fraud_related', truthy_values=fraud_truthy,
      no_values=fraud_falses)
    self.handle_boolean('active', truthy_values = ['active'], no_values = [])

    self.handle('documents', LinkDocumentsHandler)
    self.handle('categories', LinkCategoriesHandler, scope_id = CATEGORY_CONTROL_TYPE_ID)
    self.handle('assertions', LinkCategoriesHandler, scope_id = CATEGORY_ASSERTION_TYPE_ID)
    self.handle('owner', ContactEmailHandler, person_must_exist=True)
    self.handle('systems', LinkObjectControl, model_class = System)
    self.handle('processes', LinkObjectControl, model_class = Process)

  def save_object(self, db_session, **options):
    #if options.get('directive_id'):
    db_session.add(self.obj)

  def after_save(self, db_session, **options):
    if options.get('parent_type') == Directive:
      directive_id = options.get('object_id')
      for directive_control in self.obj.directive_controls:
        if directive_control.directive_id == directive_id:
          return
      db_session.add(
          DirectiveControl(directive_id=directive_id, control=self.obj))
    elif options.get('parent_type') == Program:
      program_id = options.get('object_id')
      for program_control in self.obj.program_controls:
        if program_control.program_id == program_id:
          return
      db_session.add(ProgramControl(
          program=Program.query.get(program_id),
          control=self.obj
      ))


class ControlsConverter(BaseConverter):

  metadata_map = OrderedDict([
    ('Type', 'type'),
    ('Directive Code', 'slug')
  ])

  object_map = OrderedDict([
    ('Control Code', 'slug'),
    ('Title', 'title'),
    ('Description', 'description'),
    ('Kind', 'kind'),
    ('Means', 'means'),
    ('Version', 'version'),
    ('Start Date', 'start_date'),
    ('Stop Date', 'end_date'),
    ('URL', 'url'),
    ('Map:Systems', 'systems'),
    ('Map:Processes', 'processes'),
    ('Map:Categories', 'categories'),
    ('Map:Assertions', 'assertions'),
    ('Frequency', 'verify_frequency'),
    ('Link:References', 'documents'),
    ('Map:Person of Contact', 'owner'),
    ('Key Control', 'key_control'),
    ('Active', 'active'),
    ('Fraud Related', 'fraud_related'),
    ('Created', 'created_at'),
    ('Updated' ,'updated_at')
  ])

  row_converter = ControlRowConverter

  def validate_code(self, attrs):
    if not attrs.get('slug'):
      self.errors.append('Missing {} Code heading'.format(self.parent_type_string()))
    elif attrs['slug'] != self.parent_object().slug:
      self.errors.append('{0} Code must be {1}'.format(
          self.parent_type_string(),
          self.parent_object().slug
      ))

  # Creates the correct metadata_map for the specific directive kind.
  def create_metadata_map(self):
    parent_type = self.options.get('parent_type')
    if parent_type == Directive:
      self.metadata_map = OrderedDict( [(k.replace("Directive", self.directive_kind()), v) \
                          if 'Directive' in k else (k, v) for k, v in self.metadata_map.items()] )
    elif parent_type == Program:
      self.metadata_map = OrderedDict( [(k.replace("Directive", "Program"), v) if 'Directive' in k else (k, v) \
          for k, v in self.metadata_map.items()] )

  def validate_metadata(self, attrs):
    self.validate_metadata_type(attrs, "Controls")
    self.validate_code(attrs)

  def parent_object(self):
    parent_type = self.options['parent_type']
    return parent_type.query.get(self.options['object_id'])

  def parent_type_string(self):
    return self.options.get('parent_type').__name__

  def directive_kind(self):
    parent_object = self.parent_object()
    return parent_object.kind or parent_object.meta_kind

  def do_export_metadata(self):
    yield self.metadata_map.keys()
    yield ['Controls', self.parent_object().slug]
    yield[]
    yield[]
    yield self.object_map.keys()


@@ -285,16 +285,56 @@
 rective,
+ Policy, Regulation, Contract, Standard,
  Control
@@ -394,16 +394,16 @@
 Control%0A
-
 from .ba
@@ -540,16 +540,89 @@
 YPE_ID%0A%0A
+DIRECTIVE_CLASSES = %5BDirective, Policy, Regulation, Contract, Standard%5D%0A%0A
 class Co
@@ -2663,28 +2663,36 @@
 _type') 
-== Directive
+in DIRECTIVE_CLASSES
 :%0A      
@@ -4700,20 +4700,28 @@
 ype 
-== Directive
+in DIRECTIVE_CLASSES
 :%0A  
@@ -5516,16 +5516,16 @@
 bject()%0A
+
     retu
@@ -5531,30 +5531,8 @@
 urn 
-parent_object.kind or 
 pare

Make converter expect proper directive types.