import numpy as np

from checkfield import checkfield
from fielddisplay import fielddisplay
from WriteData import WriteData


class solidearthsettings(object):
    """SOLIDEARTHSETTINGS class definition

    Usage:
        solidearthsettings = solidearthsettings()
    """

    def __init__(self, *args):  #{{{
        self.reltol                 = 0
        self.abstol                 = 0
        self.maxiter                = 0
        self.rigid                  = 0
        self.elastic                = 0
        self.rotation               = 0
        self.ocean_area_scaling     = 0
        self.runfrequency           = 1 # How many time steps we skip before we run grd_core
        self.computesealevelchange  = 1 # Will grd_core compute sea level?
        self.isgrd                  = 1 # Will GRD patterns be computed?
        self.degacc                 = 0 # Degree increment for resolution of Green tables
        self.horiz                  = 0 # Compute horizontal displacement?
        self.glfraction             = 1 # Barystatic contribution: full or fractional (default: fractional)

        nargin = len(args)

        if nargin == 0:
            self.setdefaultparameters()
        else:
            raise Exception('constructor not supported')
    #}}}

    def __repr__(self): # {{{
        s = '   solidearth settings:\n'
        s += '{}\n'.format(fielddisplay(self, 'reltol', 'sea level rise relative convergence criterion, (default, NaN: not applied)'))
        s += '{}\n'.format(fielddisplay(self, 'abstol', 'sea level rise absolute convergence criterion, NaN: not applied'))
        s += '{}\n'.format(fielddisplay(self, 'maxiter', 'maximum number of nonlinear iterations'))
        s += '{}\n'.format(fielddisplay(self, 'ocean_area_scaling', 'correction for model representation of ocean area [default: No correction]'))
        s += '{}\n'.format(fielddisplay(self, 'computesealevelchange', 'compute sealevel change from GRD in addition to steric?) default 0'))
        s += '{}\n'.format(fielddisplay(self, 'isgrd', 'compute GRD patterns (default: 1'))
        s += '{}\n'.format(fielddisplay(self, 'runfrequency', 'how many time steps we skip before we run solidearthsettings solver during transient (default: 1)'))
        s += '{}\n'.format(fielddisplay(self, 'rigid', 'rigid earth graviational potential perturbation'))
        s += '{}\n'.format(fielddisplay(self, 'elastic', 'elastic earth graviational potential perturbation'))
        s += '{}\n'.format(fielddisplay(self, 'degacc', 'accuracy (default .01 deg) for numerical discretization of the Green\'s functions'))
        s += '{}\n'.format(fielddisplay(self, 'glfraction', 'contribute fractionally (default, 1) to barystatic sea level'))
        return s
    #}}}

    def setdefaultparameters(self): # {{{
        # Convergence criterion: absolute, relative, and residual
        self.reltol = 0.01 # 1 percent
        self.abstol = np.nan # default

        # Maximum of non-linear iterations
        self.maxiter = 5

        # Computational flags
        self.rigid = 1
        self.elastic = 1
        self.rotation = 1
        self.ocean_area_scaling = 0
        self.isgrd = 1
        self.computesealevelchange = 1

        # Numerical discretization accuracy
        self.degacc = .01

        # How many time steps we skip before we run solidearthsettings solver during transient
        self.runfrequency = 1

        # Fractional contribution
        self.glfraction = 1

        # Horizontal displacement? (not on by default)
        self.horiz = 0
    #}}}

    def checkconsistency(self, md, solution, analyses): # {{{
        if ('SealevelriseAnalysis' not in analyses) or (solution == 'TransientSolution' and not md.transient.isslr):
            return md
        md = checkfield(md, 'fieldname', 'solidearth.settings.reltol', 'size', [1])
        md = checkfield(md, 'fieldname', 'solidearth.settings.abstol', 'size', [1])
        md = checkfield(md, 'fieldname', 'solidearth.settings.maxiter', 'size', [1], '>=', 1)
        md = checkfield(md, 'fieldname', 'solidearth.settings.runfrequency', 'size', [1], '>=', 1)
        md = checkfield(md, 'fieldname', 'solidearth.settings.degacc', 'size', [1], '>=', 1e-10)
        md = checkfield(md, 'fieldname', 'solidearth.settings.horiz', 'NaN', 1, 'Inf', 1, 'values', [0, 1])
        md = checkfield(md, 'fieldname', 'solidearth.settings.glfraction', 'values', [0, 1])

        # Checks on computational flags
        if self.elastic and not self.rigid:
            raise Exception('solidearthsettings checkconsistency error message: need rigid on if elastic flag is set')

        # A coupler to planet model is provided
        if self.isgrd:
            if md.transient.iscoupler:
                # We are good
                pass
            else:
                if md.mesh.__class__.__name__ == 'mesh3dsurface':
                    # We are good
                    pass
                else:
                    raise Exception('model is requesting sealevel computations without being a mesh3dsurface, or being coupled to one!')
        return md
    #}}}

    def marshall(self, prefix, md, fid): #{{{
        WriteData(fid, prefix, 'object', self, 'fieldname', 'reltol', 'name', 'md.solidearth.settings.reltol', 'format', 'Double')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'abstol', 'name', 'md.solidearth.settings.abstol', 'format', 'Double')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'maxiter', 'name', 'md.solidearth.settings.maxiter', 'format', 'Integer')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'rigid', 'name', 'md.solidearth.settings.rigid', 'format', 'Boolean')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'elastic', 'name', 'md.solidearth.settings.elastic', 'format', 'Boolean')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'rotation', 'name', 'md.solidearth.settings.rotation', 'format', 'Boolean')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'ocean_area_scaling', 'name', 'md.solidearth.settings.ocean_area_scaling', 'format', 'Boolean')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'runfrequency', 'name', 'md.solidearth.settings.runfrequency', 'format', 'Integer')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'degacc', 'name', 'md.solidearth.settings.degacc', 'format', 'Double')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'horiz', 'name', 'md.solidearth.settings.horiz', 'format', 'Integer')
        WriteData(fid, prefix, 'object', self, 'fieldname', 'computesealevelchange', 'name', 'md.solidearth.settings.computesealevelchange', 'format', 'Integer')
        WriteData(fid, prefix, 'object', self, 'fieldname','isgrd', 'name', 'md.solidearth.settings.isgrd', 'format', 'Integer')
        WriteData(fid, prefix, 'object', self, 'fieldname','glfraction', 'name', 'md.solidearth.settings.glfraction', 'format', 'Integer')
    #}}}

    def extrude(self, md): #{{{
        return self
    #}}}
