class BaseOptions(object):

    def __init__(self, **kwargs):
        self.update_dict(**kwargs)

    def __display_options__(self):
        print(json.dumps(self.__dict__, indent=4, sort_keys=True))

    def __jsonable__(self):
        return self.__dict__

    def __validate_options__(self, k, v, ov):
        if ov == NotImplemented: 
            raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        if isinstance(v,dict) and isinstance(ov,dict):
            keys = v.keys()
            if len(keys) > 1: 
                raise NotImplementedError
            return isinstance(v[keys[0]],ov[keys[0]])
        return isinstance(v, ov) 

    def update_dict(self, **kwargs):
        for k, v in kwargs.items(): 
            if k in self.ALLOWED_OPTIONS:
                # if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), SeriesOptions):
                if k in PlotOptions.ALLOWED_OPTIONS.keys():
                    if self.__getattr__(k):
                        self.__dict__[k].update(series_type=k, **v)
                    else:
                        v = SeriesOptions(series_type=k, **v)
                        self.__dict__.update({k:v})

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject):
                    if isinstance(v, dict): 
                        if self.__getattr__(k): 
                            self.__dict__[k].update(v) #update dict
                        else: # first
                            self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                    else:
                        OptionTypeError("Not An Accepted Input Type: %s, must be dictionary" % type(v))

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject):
                    if self.__getattr__(k): #existing attr
                        if isinstance(v, dict):
                            self.__dict__[k].update(v) # update array
                        elif isinstance(v, list):
                            for item in v:
                                self.__dict__[k].update(item) # update array
                        else:
                            OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary" 
                                            % type(v))          
                    else: #first 
                        if isinstance(v, dict):
                            self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                        elif isinstance(v, list):
                            if len(v) == 1:
                                self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])})
                            else:
                                self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v[0])})
                                for item in v[1:]:
                                    self.__dict__[k].update(item)
                        else:
                            OptionTypeError("Not An Accepted Input Type: %s, must be list or dictionary"
                                            % type(v))

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \
                    (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)):
                    if self.__getattr__(k): 
                        for key, value in v.items(): # check if v has object input 
                            self.__dict__[k].__options__().update({key:value})
                        
                        v = self.__dict__[k].__options__()
                    # upating object
                    if isinstance(v, dict):
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                    else:
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)})

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \
                    isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)):
                    if isinstance(v, dict):
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                    else:
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)})
                else:
                    self.__dict__.update({k:v})

            else:
                print(self.ALLOWED_OPTIONS)
                print(k, v)
                raise OptionTypeError("Not An Accepted Option Type: %s" % k)


    def __getattr__(self, item):
        if not item in self.__dict__:
            return None # Attribute Not Set
        else:
            return True class ChartOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "animation": [bool, dict, basestring],
        "backgroundColor": (ColorObject, basestring, dict),
        "borderColor": (ColorObject, basestring, dict),
        "borderRadius": [int, float],
        "borderWidth": [int, float],
        "className": basestring,
        "events": (Events, dict),
        "height": [int,basestring],
        "margin": list,
        "marginBottom": int,
        "marginLeft": int,
        "marginRight": int,
        "marginTop": int,
        "plotBackgroundColor": (ColorObject, basestring, dict),
        "plotBackgroundImage": basestring,
        "plotBorderColor": (ColorObject, basestring, dict),
        "plotBorderWidth": [int, float],
        "plotShadow": bool,
        "reflow": bool,
        "renderTo": basestring,
        "resetZoomButton": (ResetZoomButton, dict),
        "selectionMarkerFill": basestring,
        "shadow": bool,
        "spacing": list,
        "spacingBottom": int,
        "spacingLeft": int,
        "spacingRight": int,
        "spacingTop": int,
        "style": (CSSObject, dict),
        "type": basestring,
        "width": [int,basestring],
    } class ColorsOptions(BaseOptions):
    """ Special Case, this is simply just an array of colours """
    def __init__(self):
        self.colors = {}

    def set_colors(self, colors):
        if isinstance(colors, basestring):
            self.colors = ColorObject(colors)
        elif isinstance(colors, list) or isinstance(colors, dict):
            self.colors  = colors
        else:
            OptionTypeError("Not An Accepted Input Type: %s" % type(colors))

    def __jsonable__(self):
        return self.colors class ColorAxisOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "dataClassColor": basestring,
        "dataClasses": (DataClasses, dict),
        "endOnTick": bool,
        "events": (Events, dict),
        "gridLineColor": (ColorObject, basestring, dict),
        "gridLineDashStyle": basestring,
        "gridLineWidth": [float, int],
        "id": basestring,
        "labels": (Labels, dict),
        "lineColor": (ColorObject, basestring, dict),
        "lineWidth": [float, int],
        "marker": (Marker, dict),
        "max": [float, int],
        "maxColor": (ColorObject, basestring, dict),
        "maxPadding": [float, int],
        "min": [float, int],
        "minColor": (ColorObject, basestring, dict),
        "minPadding": [float, int],
        "minorGridLineColor": (ColorObject, basestring, dict),
        "minorGridLineDashStyle": basestring,
        "minorGridLineWidth": int,
        "minorTickColor": (ColorObject, basestring, dict),
        "minorTickInterval": int,
        "minorTickLength": int,
        "minorTickPosition": basestring,
        "minorTickWidth": int,
        "reversed": bool,
        "showFirstLabel": bool,
        "showLastLabel": bool,
        "startOfWeek": int,
        "startOnTick": bool,
        "stops": list,
        "tickColor": (ColorObject, basestring, dict),
        "tickInterval": int,
        "tickLength": int,
        "tickPixelInterval": int,
        "tickPosition": basestring,
        "tickPositioner": JSfunction,
        "tickPositions": list,
        "tickWidth": int,
        "type": basestring,
} class CreditsOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "enabled": bool,
        "href": basestring,
        "position": (Position, dict), 
        "style": (CSSObject, dict),
        "text": basestring,
    } class DrilldownOptions(BaseOptions): #not implement yet, need work in jinjia
    ALLOWED_OPTIONS = {
        "activeAxisLabelStyle": (CSSObject, dict),
        "activeDataLabelStyle": (CSSObject, dict),
        "animation": NotImplemented, #(bool, dict), #not sure how to implement 
        "drillUpButton": (DrillUpButton, dict),
        "series": (Series, dict),
    } class ExportingOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "buttons": (ContextButton, dict),
        "chartOptions": (ChartOptions, dict), 
        "enabled": bool,
        "filename": basestring,
        "formAttributes": NotImplemented,
        "scale": int,
        "sourceHeight": int,
        "sourceWidth": int,
        "type": basestring,
        "url": basestring,
        "width": int,
    } class GlobalOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "Date": NotImplemented,
        "VMLRadialGradientURL": basestring,
        "canvasToolsURL": basestring,
        "getTimezoneOffset": (JSfunction, basestring),
        "timezoneOffset": int,
        "useUTC": bool,
    } class LabelsOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "items": (Items, dict),
        "style": (CSSObject, dict),
    } class LangOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "decimalPoint": basestring,
        "downloadJPEG": basestring,
        "downloadPDF": basestring,
        "downloadPNG": basestring,
        "donwloadSVG": basestring,
        "exportButtonTitle": basestring,
        "loading": basestring,
        "months": list,
        "noData": basestring,
        "numericSymbols": list,
        "printButtonTitle": basestring,
        "resetZoom": basestring,
        "resetZoomTitle": basestring,
        "shortMonths": list,
        "thousandsSep": basestring,
        "weekdays": list,
    } class LegendOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "align": basestring,
        "backgroundColor": (ColorObject, basestring, dict),
        "borderColor": (ColorObject, basestring, dict),
        "borderRadius": [int, float],
        "borderWidth": [int, float],
        "enabled": bool,
        "floating": bool,
        "itemDistance": int,
        "itemHiddenStyle": (CSSObject, dict),
        "itemHoverStyle": (CSSObject, dict),
        "itemMarginBottom": int,
        "itemMarginTop": int,
        "itemStyle": (CSSObject, dict),
        "itemWidth": int,
        "labelFormat": basestring,
        "labelFormatter": (Formatter, JSfunction),
        "layout": basestring,
        "lineHeight": int,
        "margin": int,
        "maxHeight": int,
        "navigation": (Navigation, dict),
        "padding": int,
        "reversed": bool,
        "rtl": bool,
        "shadow": bool,
        "style": (CSSObject, dict),
        "symbolHeight": int,
        "symbolPadding": int,
        "symbolRadius": int,
        "symbolWidth": int,
        "title": (Title, dict),
        "useHTML": bool,
        "valueDecimals": int,
        "valueSuffix": basestring,
        "verticalAlign": basestring,
        "width": int,
        "x": int,
        "y": int,

    } class LoadingOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "hideDuration": int,
        "labelStyle": (CSSObject, dict),
        "showDuration": int,
        "style": (CSSObject, dict),
    } class MapNavigationOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "buttonOptions": (ContextButton, dict),
        "buttons": (Button, dict),
        "enableButtons": bool,
        "enableDoubleClickZoom": bool,
        "enableDoubleClickZoomTo": bool,
        "enableMouseWheelZoom": bool,
        "enableTouchZoom": bool,
        "enabled": bool,
    } class NavigationOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "buttonOptions": (ContextButton, dict),
        "menuItemHoverStyle": (CSSObject, dict),
        "menuItemStyle": (CSSObject, dict),
        "menuStyle": (CSSObject, dict),
    } class PaneOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "background": (Background, list), #arrayObject
        "center": list,
        "endAngle": int,
        "size": int,
        "startAngle": int,
    } class PlotOptions(BaseOptions):
    """ Another Special Case: Interface With all the different Highchart Plot Types Here """
    ALLOWED_OPTIONS = {
        "heatmap": (SeriesOptions, dict),
        "map": (SeriesOptions, dict),
        "mapbubble": (SeriesOptions, dict),
        "mapline": (SeriesOptions, dict),
        "mappoint": (SeriesOptions, dict),
        "series": (SeriesOptions, dict),
    } class SeriesData(BaseOptions):
    """ Another Special Case: Stores Data Series in an array for returning to the chart object """
    def __init__(self):
        #self.__dict__.update([])
        self = [] class SubtitleOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "align": basestring,
        "floating": bool,
        "style": (CSSObject, dict),
        "text": basestring,
        "useHTML": bool,
        "verticalAlign": basestring,
        "x": int,
        "y": int,
    } class TitleOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "align": basestring,
        "floating": bool,
        "margin": int,
        "style": (CSSObject, dict),
        "text": basestring,
        "useHTML": bool,
        "verticalAlign": basestring,
        "x": int,
        "y": int,
    } class TooltipOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "animation": bool,
        "backgroundColor": (ColorObject, basestring, dict),
        "borderColor": (ColorObject, basestring, dict),
        "borderRadius": [int, float],
        "borderWidth": [int, float],
        "crosshairs": [bool, list, dict],
        "enabled": bool,
        "followPointer": bool,
        "followTouchMove": bool,
        "footerFormat": basestring,
        "formatter": (Formatter, JSfunction),
        "headerFormat": basestring,
        "pointFormat": basestring,
        "positioner": (JSfunction, basestring),
        "shadow": bool,
        "style": (CSSObject, dict),
        "useHTML": bool,
        "valueDecimals": int,
        "valuePrefix": basestring,
        "valueSuffix": basestring,
    } class xAxisOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "allowDecimals": bool,
        "alternateGridColor": (ColorObject, basestring, dict),
        "endOnTick": bool, 
        "events": (Events, dict),
        "gridLineColor": (ColorObject, basestring, dict),
        "gridLineDashStyle": basestring,
        "gridLineWidth": int,
        "id": basestring,
        "labels": (Labels, dict),
        "lineColor": (ColorObject, basestring, dict),
        "lineWidth": int,
        "linkedTo": int,
        "max": [float, int],
        "maxPadding": [float, int],
        "min": [float, int],
        "minPadding": [float, int],
        "minRange": int,
        "minTickInterval": int,
        "minorGridLineColor": (ColorObject, basestring, dict),
        "minorGridLineDashStyle": basestring,
        "minorGridLineWidth": int,
        "minorTickColor": (ColorObject, basestring, dict),
        "minorTickInterval": int,
        "minorTickLength": int,
        "minorTickPosition": basestring,
        "minorTickWidth": int,
        "offset": bool,
        "opposite": bool,
        "reversed": bool,
        "showEmpty": bool,
        "showFirstLabel": bool,
        "showLastLabel": bool,
        "startOnTick": bool,
        "tickColor": (ColorObject, basestring, dict),
        "tickInterval": int,
        "tickLength": int,
        "tickPixelInterval": int,
        "tickPosition": basestring,
        "tickPositioner": JSfunction,
        "tickPositions": list,
        "tickWidth": int,
        "title": (Title, dict),
    } class yAxisOptions(BaseOptions):
    ALLOWED_OPTIONS = {
        "allowDecimals": bool,
        "alternateGridColor": (ColorObject, basestring, dict),
        "endOnTick": bool,
        "events": (Events, dict),
        "floor": (int, float),
        "gridLineColor": (ColorObject, basestring, dict),
        "gridLineDashStyle": basestring,
        "gridLineWidth": int,
        "id": basestring,
        "labels": (Labels, dict),
        "lineColor": (ColorObject, basestring, dict),
        "lineWidth": int,
        "max": [float, int],
        "maxColor": (ColorObject, basestring, dict),
        "maxPadding": [float, int],
        "min": [float, int],
        "minColor": (ColorObject, basestring, dict),
        "minPadding": [float, int],
        "minRange": int,
        "minTickInterval": int,
        "minorGridLineColor": (ColorObject, basestring, dict),
        "minorGridLineDashStyle": basestring,
        "minorGridLineWidth": int,
        "minorTickColor": (ColorObject, basestring, dict),
        "minorTickInterval": int,
        "minorTickLength": int,
        "minorTickPosition": basestring,
        "minorTickWidth": int,
        "offset": bool,
        "opposite": bool,
        "reversed": bool,
        "showEmpty": bool,
        "showFirstLabel": bool,
        "showLastLabel": bool,
        "startOnTick": bool,
        "tickColor": (ColorObject, basestring, dict),
        "tickInterval": int,
        "tickLength": int,
        "tickPixelInterval": int,
        "tickPosition": basestring,
        "tickPositioner": (JSfunction, basestring),
        "tickPositions": list,
        "tickWidth": int,
        "title": (Title, dict),  
    } class Series(object):
    """Series class for input data """

    def __init__(self, data, series_type="line", supress_errors=False, **kwargs):

        # List of dictionaries. Each dict contains data and properties, 
        # which need to handle before construct the object for series 
        if isinstance(data, list):
            for item in data:
                if isinstance(item, dict):
                    for k, v in item.items():
                        if k in DATA_SERIES_ALLOWED_OPTIONS:
                            if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]):
                                if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple):
                                    if isinstance(v, dict):
                                        item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)})
                                    elif isinstance(v, datetime.datetime):
                                        item.update({k:v})                            
                                    else:
                                        item.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)})
                                else:
                                    item.update({k:v})
                        
        self.__dict__.update({
          "data": data,
          "type": series_type,
          })

        # Series propertie can be input as kwargs, which is handled here 
        for k, v in kwargs.items():
            if k in DATA_SERIES_ALLOWED_OPTIONS:
                if SeriesOptions.__validate_options__(k,v,DATA_SERIES_ALLOWED_OPTIONS[k]):
                    if isinstance(DATA_SERIES_ALLOWED_OPTIONS[k], tuple):
                        if isinstance(v, dict):
                            self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](**v)})
                        elif isinstance(v, datetime.datetime):
                            self.__dict__.update({k:v})                            
                        else:
                            self.__dict__.update({k:DATA_SERIES_ALLOWED_OPTIONS[k][0](v)})
                    else:
                        self.__dict__.update({k:v})
                else: 
                    if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % DATA_SERIES_ALLOWED_OPTIONS[k])
            
    def __jsonable__(self):
        return self.__dict__

    def __options__(self):
        return self.__dict__ class SeriesOptions(object):
    """Class for plotOptions"""

    def __init__(self, series_type="map", supress_errors=False, **kwargs):
        self.load_defaults(series_type)
        self.process_kwargs(kwargs, series_type=series_type, supress_errors=supress_errors)

    @staticmethod
    def __validate_options__(k, v, ov):
        if isinstance(ov,list):
            if isinstance(v,tuple(ov)): return True
            else:
                raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        else:
          if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
          if isinstance(v,ov): return True
          else: return False

    def __options__(self):
        return self.__dict__

    def __jsonable__(self):
        return self.__dict__

    def __display_options__(self):
        print(json.dumps(self.__options__(),indent=4,sort_keys=True))

    def update(self,series_type, **kwargs):
        allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type]
        allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"])

        for k, v in kwargs.items():
            if k in allowed_args:
                if SeriesOptions.__validate_options__(k,v,allowed_args[k]):
                    if isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), CommonObject):
                        # re-construct input dict with existing options in objects
                        if self.__getattr__(k):
                            if isinstance(v, dict):
                                self.__options__()[k].update(v)
                            else:
                                self.__options__()[k].__options__().update(v)
                        else:
                            self.__options__().update({k:allowed_args[k][0](**v)}) 

                    elif isinstance(allowed_args[k], tuple) and isinstance(allowed_args[k][0](), ArrayObject):
                        # update array 
                        if isinstance(v, dict):
                            self.__dict__[k].append(allowed_args[k][0](**v))
                        elif isinstance(v, list):
                            for item in v:
                                self.__dict__[k].append(allowed_args[k][0](**item))
                        else:
                            OptionTypeError("Not An Accepted Input Type: %s" % type(v))        

                    elif isinstance(allowed_args[k], tuple) and \
                        (isinstance(allowed_args[k][0](), CSSObject) or isinstance(allowed_args[k][0](), SVGObject)):
                        if self.__getattr__(k):
                            for key, value in v.items():
                                self.__dict__[k].__options__().update({key:value})
                        else:
                            self.__dict__.update({k:allowed_args[k][0](**v)})
                        
                        v = self.__dict__[k].__options__()
                        # upating object
                        if isinstance(v, dict):
                            self.__dict__.update({k:allowed_args[k][0](**v)})
                        else:
                            self.__dict__.update({k:allowed_args[k][0](v)})

                    elif isinstance(allowed_args[k], tuple) and (isinstance(allowed_args[k][0](), JSfunction) or \
                        isinstance(allowed_args[k][0](), Formatter) or isinstance(allowed_args[k][0](), ColorObject)):
                        if isinstance(v, dict):
                            self.__dict__.update({k:allowed_args[k][0](**v)})
                        else:
                            self.__dict__.update({k:allowed_args[k][0](v)})
                    else:
                        self.__dict__.update({k:v})
            else: 
                print(k,v)
                if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k])
                

    def process_kwargs(self, kwargs, series_type, supress_errors=False):
        allowed_args = PLOT_OPTION_ALLOWED_ARGS[series_type]
        allowed_args.update(PLOT_OPTION_ALLOWED_ARGS["common"])

        for k, v in kwargs.items():
            if k in allowed_args:
                if SeriesOptions.__validate_options__(k,v,allowed_args[k]):
                    if isinstance(allowed_args[k], tuple):
                        if isinstance(v, dict):
                            self.__dict__.update({k:allowed_args[k][0](**v)})
                        elif isinstance(v, list):
                            if len(v) == 1:
                                self.__dict__.update({k:allowed_args[k][0](**v[0])})
                            else:
                                self.__dict__.update({k:allowed_args[k][0](**v[0])})
                                for item in v[1:]:
                                    self.__dict__[k].update(item)
                        elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \
                            isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \
                            isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime):
                            self.__dict__.update({k:v})                           
                        else:
                            self.__dict__.update({k:allowed_args[k][0](v)})
                    else:
                        self.__dict__.update({k:v})          
                else: 
                    print(k,v)
                    if not supress_errors: raise OptionTypeError("Option Type Mismatch: Expected: %s" % allowed_args[k])

    def load_defaults(self, series_type):
        self.process_kwargs(DEFAULT_OPTIONS.get(series_type,{}),series_type)

    def __getattr__(self, item):
        if not item in self.__dict__:
            return None # Attribute Not Set
        else:
            return True class Formatter(object):
    """ Base Formatter Class """

    def __init__(self, format=None):
        ### Choose either from default functions in FORMATTER_TYPE_MAPPINGS using format_type 
        ### or wriet a function in format_string
        if format:
            if format in FORMATTER_TYPE_MAPPINGS:
                self.formatter = RawJavaScriptText(FORMATTER_TYPE_MAPPINGS[format])
            elif isinstance(format, basestring):
                self.formatter = RawJavaScriptText(format)
            else:
                raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring)

    def __options__(self):
        return self.formatter

    def __jsonable__(self):
        return self.formatter class CSSObject(object):
    """ CSS style class """

    def __init__(self, **kwargs):
        self.css = kwargs

        for k, v in self.css.items():
            if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]):
                v = RawJavaScriptText(v)
                self.css.update({k:v})

    def __options__(self):
        return self.css

    def __jsonable__(self):
        return self.css class SVGObject(object):
    """ SVG style class """

    def __init__(self, **kwargs):
        self.svg = kwargs

        for k, v in self.svg.items():
            if isinstance(v, basestring) and any([REGEX_LIST[key].search(v) for key in REGEX_LIST.keys()]):
                v = RawJavaScriptText(v)
                self.svg.update({k:v})
    
    def __options__(self):
        return self.svg

    def __jsonable__(self):
        return self.svg class MapObject(object):
    """ Base Map Class """

    def __init__(self, mapdata=None):
        if mapdata:
            if isinstance(mapdata, basestring):
                self.map = RawJavaScriptText(mapdata)
            elif isinstance(mapdata, JSfunction):
                self.map = mapdata
            else:
                raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring)

    def __options__(self):
        return self.map

    def __jsonable__(self):
        return self.map class JSfunction(object):

    def __init__(self, function = None):
        if function:
            if isinstance(function, basestring):
                self.function = RawJavaScriptText(function)
            elif isinstance(function, JSfunction):
                self.function = function
            else:
                raise OptionTypeError("Option Type Mismatch: Expected: %s" % basestring)

    def __options__(self):
        return self.function

    def __jsonable__(self):
        return self.function class RawJavaScriptText:

    def __init__(self, jstext):
        self._jstext = jstext   
    def get_jstext(self):
        return self._jstext class CommonObject(object):

    def __init__(self, **kwargs):
        self.process_kwargs(kwargs)

    def __validate_options__(self, k,v,ov):

        if ov == NotImplemented: 
            raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        if isinstance(ov,list):
            if isinstance(v,tuple(ov)): return True
            else:
                raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        else:
            if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
            if isinstance(v,ov): return True
            else: return False

    def __options__(self):
        return self.__dict__

    def __jsonable__(self):
        return self.__dict__

    def update(self, kwargs):
        for k, v in kwargs.items(): 
            if k in self.ALLOWED_OPTIONS:
                if isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), CommonObject):
                    # re-construct input dict with existing options in objects
                    if self.__getattr__(k):
                        if isinstance(v, dict):
                            self.__options__()[k].update(v)
                        else:
                            self.__options__()[k].__options__().update(v)
                    else:
                        self.__options__().update({k:allowed_args[k][0](**v)}) 

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and isinstance(self.ALLOWED_OPTIONS[k][0](), ArrayObject):
                    # update array 
                    if isinstance(v, dict):
                        self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**v))
                    elif isinstance(v, list):
                        for item in v:
                            self.__dict__[k].append(self.ALLOWED_OPTIONS[k][0](**item))
                    else:
                        OptionTypeError("Not An Accepted Input Type: %s" % type(v))        

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and \
                    (isinstance(self.ALLOWED_OPTIONS[k][0](), CSSObject) or isinstance(self.ALLOWED_OPTIONS[k][0](), SVGObject)):
                    if self.__getattr__(k):
                        for key, value in v.items():
                            self.__dict__[k].__options__().update({key:value})
                    else:
                        self.__dict__.update({k:allowed_args[k][0](**v)})
                    
                    v = self.__dict__[k].__options__()
                    # upating object
                    if isinstance(v, dict):
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                    else:
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)})

                elif isinstance(self.ALLOWED_OPTIONS[k], tuple) and (isinstance(self.ALLOWED_OPTIONS[k][0](), JSfunction) or \
                    isinstance(self.ALLOWED_OPTIONS[k][0](), Formatter) or isinstance(self.ALLOWED_OPTIONS[k][0](), ColorObject)):
                    if isinstance(v, dict):
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                    else:
                        self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)})
                else:
                    self.__dict__.update({k:v})

            else:
                raise OptionTypeError("Not An Accepted Option Type: %s" % k)

    def process_kwargs(self,kwargs):
        
        for k, v in kwargs.items():
            if k in self.ALLOWED_OPTIONS:
                if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]):
                    if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \
                        self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST:
                        if isinstance(v, dict):
                            self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                        elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \
                            isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \
                            isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime):
                            self.__dict__.update({k:v})
                        else:
                            self.__dict__.update({k:self.ALLOWED_OPTIONS[k][0](v)})
                    else:
                        self.__dict__.update({k:v})
                else:
                    raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k])
            else:
                raise OptionTypeError("Option: %s Not Allowed For CommonObject:" % k) class ArrayObject(object):

    def __init__(self, **kwargs):
        self.data = []
        self.process_kwargs(kwargs)

    def __validate_options__(self, k,v,ov):
 
        if ov == NotImplemented: 
            raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        if isinstance(ov,list):
            if isinstance(v,tuple(ov)): return True
            else:
                raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
        else:
            if ov == NotImplemented: raise OptionTypeError("Option Type Currently Not Supported: %s" % k)
            if isinstance(v,ov): return True
            else: return False

    def __options__(self):
        return self.data

    def __jsonable__(self):
        return self.data

    def update(self, kwargs):
        self.process_kwargs(kwargs)

    def process_kwargs(self,kwargs):
        temp = {}
        for k, v in kwargs.items():
            if k in self.ALLOWED_OPTIONS:
                if self.__validate_options__(k,v,self.ALLOWED_OPTIONS[k]):
                    if isinstance(self.ALLOWED_OPTIONS[k], tuple) and \
                        self.ALLOWED_OPTIONS[k][0] in IDV_OBJECT_LIST:
                        if isinstance(v, dict):
                            temp.update({k:self.ALLOWED_OPTIONS[k][0](**v)})
                        elif isinstance(v, CommonObject) or isinstance(v, ArrayObject) or \
                            isinstance(v, CSSObject) or isinstance(v, SVGObject) or isinstance(v, ColorObject) or \
                            isinstance(v, JSfunction) or isinstance(v, Formatter) or isinstance(v, datetime.datetime):
                            temp.update({k:v})
                        else:
                            temp.update({k:self.ALLOWED_OPTIONS[k][0](v)})
                    else:
                        temp.update({k:v})   
                else: 
                    raise OptionTypeError("Option Type Mismatch: Expected: %s" % self.ALLOWED_OPTIONS[k])
            else: 
                raise OptionTypeError("Option: %s Not Allowed For ArrayObject:" % k)
        self.data.append(temp) class ColorObject(object):
    """ color object """
    
    def __init__(self, color=None, **kwargs):
        if not color:
            color = kwargs
        
        if color:
            if isinstance(color, dict):
                tmp = []            
                for i, item in enumerate(color['stops']):
                    tmp.append([RawJavaScriptText(x) if isinstance(x, basestring) and any([REGEX_LIST[key].search(x) for key in REGEX_LIST.keys()]) 
                                else x for x in item ])
                color['stops'] = tmp
                self.color = color
            elif any([REGEX_LIST[key].search(color) for key in REGEX_LIST.keys()]):
                self.color = RawJavaScriptText(color)
            elif isinstance(color, basestring):
                self.color = color
            else:
                raise OptionTypeError("Option Type Mismatch: Expected: %s" % (basestring or dict))
        else:
            self.color = None

    def __options__(self):
        return self.color

    def __jsonable__(self):
        return self.color
from future.standard_library import install_aliases
from past.builtins import basestring
from urllib.request import urlopen
from jinja2 import Environment, PackageLoader
from collections import Iterable
from .options import BaseOptions, ChartOptions, \
    ColorsOptions, ColorAxisOptions, CreditsOptions, DrilldownOptions, ExportingOptions, \
    GlobalOptions, LabelsOptions, LangOptions, \
    LegendOptions, LoadingOptions, MapNavigationOptions, NavigationOptions, PaneOptions, \
    PlotOptions, SeriesData, SubtitleOptions, TitleOptions, \
    TooltipOptions, xAxisOptions, yAxisOptions
from .highmap_types import Series, SeriesOptions
from .common import Formatter, CSSObject, SVGObject, MapObject, JSfunction, RawJavaScriptText, \
    CommonObject, ArrayObject, ColorObject
import json, uuid
import re
import datetime
import html
        self.setoption = json.dumps(self.setOptions, cls = HighchartsEncoder) 
        self.data = json.dumps(self.data_temp, cls = HighchartsEncoder)
        
        if self.drilldown_flag: 
            self.drilldown_data = json.dumps(self.drilldown_data_temp, cls = HighchartsEncoder)
        self._htmlcontent = self.template_content_highcharts.render(chart=self).encode('utf-8')


    def buildhtml(self):
        """Build the HTML page
        Create the htmlheader with css / js
        Create html page
        """
        self.buildcontent()
        self.buildhtmlheader()
        self.content = self._htmlcontent.decode('utf-8') # need to ensure unicode
        self._htmlcontent = self.template_page_highcharts.render(chart=self)
        return self._htmlcontent

    def buildhtmlheader(self):
        """generate HTML header content"""
        #Highcharts lib/ needs to make sure it's up to date
        
        if self.drilldown_flag:
            self.add_JSsource('https://code.highcharts.com/maps/modules/drilldown.js')

        self.header_css = [
            '<link href="%s" rel="stylesheet" />' % h for h in self.CSSsource
        ]

        self.header_js = [
            '<script type="text/javascript" src="%s"></script>' % h for h in self.JSsource
        ]

        self.htmlheader = ''
        for css in self.header_css:
            self.htmlheader += css
        for js in self.header_js:
            self.htmlheader += js


    def buildcontainer(self):
        """generate HTML div"""
        if self.container:
            return
        # Create HTML div with style
        if self.options['chart'].width:
            if str(self.options['chart'].width)[-1] != '%':
                self.div_style += 'width:%spx;' % self.options['chart'].width
            else:
                self.div_style += 'width:%s;' % self.options['chart'].width
        if self.options['chart'].height:
            if str(self.options['chart'].height)[-1] != '%':
                self.div_style += 'height:%spx;' % self.options['chart'].height
            else:
                self.div_style += 'height:%s;' % self.options['chart'].height

        self.div_name = self.options['chart'].__dict__['renderTo'] # recheck div name
        self.container = self.containerheader + \
            '<div id="%s" style="%s">%s</div>\n' % (self.div_name, self.div_style, self.loading)

    @property
    def htmlcontent(self):
        return self.buildhtml()

    @property
    def iframe(self):
        htmlsrcdoc = html.escape(self.htmlcontent)
        htmlsrcdoc = re.sub('\\n', ' ', htmlsrcdoc)
        htmlsrcdoc = re.sub(' +', ' ', htmlsrcdoc)
        width = int(self.options['chart'].__dict__['width']) if self.options['chart'].__dict__.get('width') else 820
        height = int(self.options['chart'].__dict__['height']) if self.options['chart'].__dict__.get('height') else 520
        
        if self.options['chart'].__dict__.get('options3d'):
            if len(htmlsrcdoc) < 99965000 :
                return '<iframe style="border:0;outline:none;overflow:hidden" src="data:text/html,'+ htmlsrcdoc + '" height=' + str(height) + \
                        ' width=' + str(width) + '></iframe>'
            else:
                return '<iframe style="border:0;outline:none;overflow:hidden" srcdoc="'+ htmlsrcdoc + '" height='+ str(height) + ' width=' + str(width) + '></iframe>'
        else:
            return '<iframe style="border:0;outline:none;overflow:hidden" srcdoc="'+ htmlsrcdoc + '" height='+ str(height) + ' width=' + str(width) + '></iframe>'
    
    def __str__(self):
        """return htmlcontent"""
        #self.buildhtml()
        return self.htmlcontent

    def save_file(self, filename = 'Map'):
        """ save htmlcontent as .html file """
        filename = filename + '.html'
        
        with open(filename, 'w') as f:
            #self.buildhtml()
            f.write(self.htmlcontent)
        
        f.closed

class HighchartsEncoder(json.JSONEncoder):
    def __init__(self, *args, **kwargs):
        json.JSONEncoder.__init__(self, *args, **kwargs)
        self._replacement_map = {}

    def default(self, obj):
        if isinstance(obj, RawJavaScriptText):
            key = uuid.uuid4().hex
            self._replacement_map[key] = obj.get_jstext()
            return key
        elif isinstance(obj, datetime.datetime):
            utc = obj.utctimetuple()
            obj = (u"Date.UTC({year},{month},{day},{hours},{minutes},{seconds},{millisec})"
                    .format(year=utc[0], month=utc[1]-1, day=utc[2], hours=utc[3],
                            minutes=utc[4], seconds=utc[5], millisec=obj.microsecond/1000))
            return RawJavaScriptText(obj)
        elif isinstance(obj, BaseOptions):
            return obj.__jsonable__()
        elif isinstance(obj, CSSObject) or isinstance(obj, Formatter) or isinstance(obj, JSfunction) \
            or isinstance(obj, MapObject): 
            return obj.__jsonable__()
        elif isinstance(obj, SeriesOptions) or isinstance(obj, Series):
            return obj.__jsonable__()

        elif isinstance(obj, CommonObject) or isinstance(obj, ArrayObject) or isinstance(obj, ColorObject):