#!/usr/bin/env python
# encoding: utf-8
"""
Title: silviculture

Silvicultural operation model implementations for SIMO simulator ver. 0.2.0a

*silviculture.py*

Copyright (c) 2007 Simosol Oy. Distributed under the GNU General Public License 2.0.

NOTE:
    All Python operation models share the same set of input parameters. When
    implementing new operation model, remember to assign the following list
    of parameters as the function parameters:
        data - data by level
        variables - operation variable values
        parameters - operation parameter values
        parameter_table_values - values retrieved from a parameter table
        cash_flow_table - cash_flow structure
        cash_flow - container for returning cash flow (sum)
        results - container (numpy array) for returning operation results
        warnings - container for returning warnings
        errors - container for returning errors
"""

# Imports
from silviculturedata import SilvicultureData
from silvicultureop import SilvicultureOperation
from constants import EPSILON, TREELABEL, STRATUMLABEL, STANDLABEL

############################################################
##  Operation functions, callable from outside of module
############################################################

def planting(data, variables, parameters, parameter_table_values,
             cash_flow_table, cash_flow, results, restype, warnings, errors,
             price_cache, stem_cache):
    """
    Planting
    """
    sd = SilvicultureData('planting', data, variables, parameters,
                     parameter_table_values, cash_flow_table)
    if not sd.valid:
        msg = 'Missing parameter table data for silvicultural operation'
        errors.append(msg)
        return -1
    sop = SilvicultureOperation(sd)
    results[0] = 1  # Age
    results[1] = 0  # A
    results[2] = sd.cdata['targetdensity'] # N
    results[3] = 0  # BA
    results[4] = sop.regen_sp # SP
    results[5] = 0  # D_gM
    results[6] = 0  # H_gM
    results[7] = 0  # H_dom
    results[8] = 0  # V
    results[9] = 0  # V_old
    results[10] = 0  # N_tree
    results[11] = 0  # N_theor
    results[12] = 0  # N_ingrowth
    results[13] = 0  # RDF
    results[14] = 0  # Upper_st
    results[15] = 0  # trees_generated
    results[16] = 1  # seedling_below13
    results[17] = 1  # artificial_ingrowth
    results[18] = 0  # trees_just_generated
    results[19] = 0  # non_seedling
    results[20] = 0  # seedtree_stratum
    results[21] = 0  # natural_ingrowth
    results[22] = 0  # stand_value
    results[23] = 0  # COMWOOD
    results[24] = 0  # LOGp
    results[25] = 0  # i_Vm3
    results[26] = 0  # i_Vpros
    results[27] = 0  # a_0
    results[28] = 0  # V_PULP
    results[29] = 0  # V_LOG
    results[30] = 0  # N_measured
    results[31] = 0  # only_N_measured
    results[32] = 0  # BM_stems
    results[33] = 0  # BM_branches
    results[34] = 0  # BM_stumps
    results[35] = 0  # BM_foliage
    results[36] = 0  # BM_roots
    results[37] = 0  # BM_fine_roots
    results[38] = 0  # BM_total
    results[39] = 0  # Yield
    results[40] = 0  # Carbon_flux_natural_removal
    results[41] = 0  # Carbon_flux_natural_removal_energy

    return 1

def seeding(data, variables, parameters, parameter_table_values,
            cash_flow_table, cash_flow, results, restype, warnings, errors,
            price_cache, stem_cache):
    """
    Seeding. Implementation of the model is the same as in
    planting, only operation name is different.
    """
    return planting(data, variables, parameters, parameter_table_values,
             cash_flow_table, cash_flow, results, restype, warnings, errors,
             price_cache, stem_cache)

def supplemental_planting(data, variables, parameters, parameter_table_values,
                          cash_flow_table, cash_flow, results, restype,
                          warnings, errors, price_cache, stem_cache):
    """
    Supplemental_planting
    """
    sd = SilvicultureData('supplemental_planting', data, variables, parameters,
                     parameter_table_values, cash_flow_table)
    if not sd.valid:
        msg = 'Missing parameter table data for silvicultural operation'
        errors.append(msg)
        return -1
    sop = SilvicultureOperation(sd)
    # Target density after supplemental planting (original planting density)
    targetdensity = sd.cdata['targetdensity']
    # Calculate treecount and define stem number for planting
    suppseedlingsnum, SP = sop.define_stem_number_for_supplemental_planting(targetdensity)

    results[0] = 1  # Age
    results[1] = 0  # A
    results[2] = suppseedlingsnum
    results[3] = 0  # BA
    results[4] = SP  # SP
    results[5] = 0  # D_gM
    results[6] = 0  # H_gM
    results[7] = 0  # H_dom
    results[8] = 0  # V
    results[9] = 0  # V_old
    results[10] = 0  # N_tree
    results[11] = 0  # N_theor
    results[12] = 0  # N_ingrowth
    results[13] = 0  # RDF
    results[14] = 0  # Upper_st
    results[15] = 0  # trees_generated
    results[16] = 1  # seedling_below13
    results[17] = 1  # artificial_ingrowth
    results[18] = 0  # trees_just_generated
    results[19] = 0  # non_seedling
    results[20] = 0  # seedtree_stratum
    results[21] = 0  # natural_ingrowth
    results[22] = 0  # stand_value
    results[23] = 0  # COMWOOD
    results[24] = 0  # LOGp
    results[25] = 0  # i_Vm3
    results[26] = 0  # i_Vpros
    results[27] = 0  # a_0
    results[28] = 0  # V_PULP
    results[29] = 0  # V_LOG
    results[30] = 0  # N_measured
    results[31] = 0  # only_N_measured
    results[32] = 0  # BM_stems
    results[33] = 0  # BM_branches
    results[34] = 0  # BM_stumps
    results[35] = 0  # BM_foliage
    results[36] = 0  # BM_roots
    results[37] = 0  # BM_fine_roots
    results[38] = 0  # BM_total
    results[39] = 0  # Yield
    results[40] = 0  # Carbon_flux_natural_removal
    results[41] = 0  # Carbon_flux_natural_removal_energy

    treecount = sop.treecount
    origtreecount = sop.origtreecount
    # Update stem number and indicate modification
    treecount = treecount + suppseedlingsnum
    ret = sd.update_stem_number(treecount, origtreecount)
    return 1

def tending_of_seedling_stand(data, variables, parameters,
                              parameter_table_values, cash_flow_table,
                              cash_flow, results, restype, warnings, errors,
                              price_cache, stem_cache):
    """
    Tending of seedling stand. Tending operation removes trees
    from the stand until the target density (trees/ha) is reached. The order in
    which the trees are removed is based on the removal order of different
    species.
    """

    # check that operation data actually exists
    for level, ldata in data.iteritems():
        if ldata is None:
            errors.append("The level %s doesn't have data!")
            return -1

    sd = SilvicultureData('tending', data, variables, parameters,
                     parameter_table_values, cash_flow_table)
    if not sd.valid:
        msg = 'Missing parameter table data for silvicultural operation'
        errors.append(msg)
        return -1

    # Check that the current number of trees on stratum level is the same as sum of
    # stem numbers on tree level
    stem_number_tree = sd.get_real_stem_numbers_for_stratums()
    changed = False
    stem_number_changed = 0
    idind = sd.ind['stratum_id']
    Nind = sd.ind['stratum_N']
    for strat in data[STRATUMLABEL]:
        if strat[idind] in stem_number_tree:
            if abs(strat[Nind] - stem_number_tree[strat[idind]]) > EPSILON:
                strat[Nind] = stem_number_tree[strat[idind]]
                changed = True
        stem_number_changed += strat[Nind]
    if changed:
        #Update value on comp_unit level to original data
        data[STANDLABEL][0][3] = stem_number_changed
        sd.cdata['N'] = stem_number_changed

    sop = SilvicultureOperation(sd)

    # Check that the current number of trees is higher than target density
    treecount = sop.treecount
    targetdensity = sd.cdata['targetdensity']
    preferenceorder = sd.cdata['preferenceorder']
    if (treecount - targetdensity) < EPSILON:
        return 1
    # Conclude which strata are 'artificial' and which 'natural' and at the same
    # time remove strata without trees
    natstrat, artstrat, treecount = sd.separate_strata_and_remove_treeless(\
        treecount)
    # Check if target density has been reached
    ret = sd.targetdensity_reached(treecount)
    if ret == 1:
        return ret
    # Get a listing of trees for each stratum
    treenums = sd.get_diamclasses_for_stratums()
    trees = sd.create_sorted_objectlist()
    # Remove trees from strata in the removal order (last item of the list)
    while len(preferenceorder) > 0:
        sptoremove = preferenceorder.pop()
        # First remove the natural regeneration
        treecount = sop.remove_trees(natstrat, trees, treenums, sptoremove,
                                     treecount, sd)
        # Then remove the artificial regeneration
        treecount = sop.remove_trees(artstrat, trees, treenums, sptoremove,
                                     treecount, sd)
        ret = sd.targetdensity_reached(treecount)
        if ret == 1:
            return ret
    return 1

def early_tending_of_seedling_stand(data, variables, parameters,
                                    parameter_table_values, cash_flow_table,
                                    cash_flow, results, restype, warnings,
                                    errors, price_cache, stem_cache):
    """
    Early tending of seedling stand. Implementation of the model is the same as in
    tending_of_seedling_stand, only opeartion name and target density are different.
    """
    return tending_of_seedling_stand(data, variables, parameters,
                              parameter_table_values, cash_flow_table,
                              cash_flow, results, restype, warnings, errors,
                              price_cache, stem_cache)

def improvement_of_young_stand(data, variables, parameters,
                               parameter_table_values, cash_flow_table,
                               cash_flow, results, restype, warnings, errors,
                               price_cache, stem_cache):
    """
    Improvement_of_young_stand. Implementation of the model is the same as in
    tending_of_seedling_stand, only operation name and target density are different.
    """
    return tending_of_seedling_stand(data, variables, parameters,
                              parameter_table_values, cash_flow_table,
                              cash_flow, results, restype, warnings, errors,
                              price_cache, stem_cache)


