ColorScheme
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description A color scheme generator.
library(title='ColorScheme')



//#region   -> Helpers:

import RicardoSantos/ColorExtension/1 as colex

// Constants: {

// Color Model names:
string __CMODEL1__ = 'monochromatic'
string __CMODEL2__ = 'analog'
string __CMODEL3__ = 'triadic'
string __CMODEL4__ = 'tetradic'
string __CMODEL5__ = 'square'

// }

//  () {

// @function Enumerate models available to profile colors.
// @param index `int`: Index of color model. (1:'monochromatic', 2:'analog', 3:'triadic', 4:'tetradic', 5:'square', anything else:'monochromatic')
// @returns
// - `string`: Name of color model.
// ---
// Usage:
// - `model(1)`
export model (int index=0) =>
    switch index
        1 => __CMODEL1__
        2 => __CMODEL2__
        3 => __CMODEL3__
        4 => __CMODEL4__
        5 => __CMODEL5__
        => __CMODEL1__

// }
// model_index () {

// @function Enumerate a model name to a index number.
// @param model `string`: Name of the model
// @returns
// - `int`: Index of color model.
// ---
// Usage:
// - `model_index("monochromatic")`
export model_index (string model)=>
    switch model
        __CMODEL1__ => 1
        __CMODEL2__ => 2
        __CMODEL3__ => 3
        __CMODEL4__ => 4
        __CMODEL5__ => 5
        => 1

// }

//#endregion



//#region   -> Constructor:

// @type Theme object holding a dictionary alike structure.
// @field keys      List of named keys.
// @field colors    List of colors.
export type Theme
    array<string> keys
    array<color>  colors

// init () {

// @function Initiate the array data registry that will hold the color profile.
// @param this  `Theme`: Theme object.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().init()`
export method init (Theme this) =>
    var string[] _keys   = array.new_string(0)
    var color[]  _colors = array.new_color(0)
    this.keys   := _keys
    this.colors := _colors
    this

// }
// new () {

// @function Generate and initiate the properties of a new theme object.
// @returns
// - New theme object.
// ---
// Usage:
// - `theme = new()`
export new () =>
    Theme.new().init()

// }

//#endregion



//#region   -> Testing:

// check_registry_integrity () {

// @function Checks the integrity of the registers, it will produce a runtime error if check fails.
// @param this  `Theme`: Theme object.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().check_registry_integrity()`
export method check_registry_integrity (Theme this) =>
    int _size_k = array.size(this.keys)
    int _size_c = array.size(this.colors)
    if _size_k != _size_c
        runtime.error('ColorScheme -> check_registry_integrity(): registers size do not match.')
    this

// }

//#endregion



//#region   -> Properties:

// add () {

// @function Add new (key, color) entry to the registry.
// @param this  `Theme` : Theme object.
// @param key   `string`: Unique key to reference the value.
// @param value `color` : Color value of the specified key.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme.new().add("Gray20", #333333)`
export method add (Theme this, string key, color value) =>
    check_registry_integrity(this)
    int is_key_in = array.indexof(this.keys, key)
    if is_key_in > -1
        runtime.error('ColorScheme -> add(): key is already present in registry.')
    else
        array.push(this.keys, key)
        array.push(this.colors, value)
    this

// }
//  () {

// @function Get a (key, color) entry from the registry.
// @param this  `Theme` : Theme object.
// @param key   `string`: Unique key to reference the value.
// @returns
// - `color`: The color at referenced key.
// ---
// Usage:
// - `theme = Theme.new().add("Gray20", #333333).get_color("Gray20")`
export method get_color (Theme this, string key) =>
    check_registry_integrity(this)
    int _key_index = array.indexof(this.keys, key)
    if _key_index < 0
        runtime.error('ColorScheme -> delete(): key is not present in registry.')
    array.get(this.colors, _key_index)

// }
//  () {

// @function Edit a (key, color) entry in the registry.
// @param this      `Theme` : Theme object.
// @param key       `string`: Unique key to reference the value, key target to update.
// @param new_key   `string`: Unique key to reference the value.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().add("Gray20", #333333).edit_key("Gray20", "Gray")`
export method edit_key (Theme this, string key, string new_key) =>
    check_registry_integrity(this)
    int _key_index     = array.indexof(this.keys, key)
    int _new_key_index = array.indexof(this.keys, new_key)
    switch 
        _key_index < 0      => runtime.error('ColorScheme -> edit_key(): key is not present in registry.')
        _new_key_index > -1 => runtime.error('ColorScheme -> edit_key(): new key is already present in registry.')
    array.set(this.keys, _key_index, new_key)
    this

// }
//  () {

// @function Edit a (key, color) entry in the registry.
// @param this      `Theme` : Theme object.
// @param key       `string`: Unique key to reference the value.
// @param new_value `color` : Color value of the specified key.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().add("Gray20", #333333).edit_color("Gray20", #222222)`
export method edit_color (Theme this, string key, color new_value) =>
    check_registry_integrity(this)
    int _key_index = array.indexof(this.keys, key)
    if _key_index < 0
        runtime.error(str.format('ColorScheme -> edit_color({0}): key is not present in registry.', key))
    else
        array.set(this.colors, _key_index, new_value)
    this

// }
//  () {

// @function Delete a (key, color) entry from the registry.
// @param this  `Theme` : Theme object.
// @param key   `string`: Unique key to reference the value.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().add("Gray20", #333333).delete("Gray20")`
export method delete (Theme this, string key) =>
    check_registry_integrity(this)
    int _key_index = array.indexof(this.keys, key)
    if _key_index < 0
        runtime.error(str.format('ColorScheme -> delete({0}): key is not present in registry.', key))
        color(na)
    else
        array.remove(this.keys, _key_index)
        array.remove(this.colors, _key_index)
    this

// }
//  () {

// @function Delete all (key, color) entrys from the registry.
// @param this  `Theme`: Theme object.
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().delete_all()`
export method delete_all (Theme this) =>
    array.clear(this.keys)
    array.clear(this.colors)
    this

// }

//#endregion



//#region   -> Model generation:

//  () {

// @function Generate a multi color scheme.
// @param this      `Theme` : Theme object.
// @param primary   `color` : Origin color to base the profile.
// @param model     `string`: Color model selection, default='monochromatic', options=('monochromatic', 'triadic near', 'triadic far', 'tetradic')
// @returns
// - `this`: Self, Theme object.
// ---
// Usage:
// - `theme = Theme.new().generate_scheme(#e3e3e3, "monochromatic")`
export method generate_scheme(Theme this, color primary, string model='monochromatic') =>
    check_registry_integrity(this)
    // delete_all(key_registry, color_registry) // possible implementation to clear registry conflicts?
    this.add('primary 0', primary)
    // primary color hsl properties:
    [_primary_hue, _primary_saturation, _primary_lightness] = colex.rgb_to_hsl(color.r(primary), color.g(primary), color.b(primary))
    switch model
        __CMODEL1__ =>
            // monochromatic generates 4 colors: primary + 3 shades to the most distant edge (dark, light)
            // generate shades to dark or light
            if _primary_lightness >= 50.0
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.75, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.25, 0.0))
            else
                float _inverse_range = 100.0 - _primary_lightness
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + _inverse_range * 0.25, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + _inverse_range * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + _inverse_range * 0.75, 0.0))
        __CMODEL2__ =>
            // analog generates 2 adjacent base colors and 3 shades to the most distant edge (dark, light) for each
            [_lower, _upper] = colex.analog(primary)
            [_lower_hue, _lower_saturation, _lower_lightness] = colex.rgb_to_hsl(color.r(_lower), color.g(_lower), color.b(_lower))
            [_upper_hue, _upper_saturation, _upper_lightness] = colex.rgb_to_hsl(color.r(_upper), color.g(_upper), color.b(_upper))
            // generate shades to dark or light
            if _primary_lightness >= 50.0
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.75, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.25, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.75, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.25, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.75, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.25, 0.0))
            else
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.25, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.75, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.25, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.75, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.25, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.75, 0.0))
        __CMODEL3__ =>
            // triadic generates 2 far spectrum base colors and 3 shades to the most distant edge (dark, light) for each
            [_lower, _upper] = colex.triadic(primary)
            [_lower_hue, _lower_saturation, _lower_lightness] = colex.rgb_to_hsl(color.r(_lower), color.g(_lower), color.b(_lower))
            [_upper_hue, _upper_saturation, _upper_lightness] = colex.rgb_to_hsl(color.r(_upper), color.g(_upper), color.b(_upper))
            // generate shades to dark or light
            if _primary_lightness >= 50.0
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.75, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.25, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.75, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.25, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.75, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.25, 0.0))
            else
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.25, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.75, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.25, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.75, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.25, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.75, 0.0))
        __CMODEL4__ =>
            // tetradic generates 3 complement colors in a rectangular shape and 3 shades for each.
            [_complement, _lower, _upper] = colex.tetradic(primary)
            [_complement_hue, _complement_saturation, _complement_lightness] = colex.rgb_to_hsl(color.r(_complement), color.g(_complement), color.b(_complement))
            [_lower_hue, _lower_saturation, _lower_lightness] = colex.rgb_to_hsl(color.r(_lower), color.g(_lower), color.b(_lower))
            [_upper_hue, _upper_saturation, _upper_lightness] = colex.rgb_to_hsl(color.r(_upper), color.g(_upper), color.b(_upper))
            // generate shades to dark or light
            if _primary_lightness >= 50.0
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.75, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.25, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.75, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.25, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.75, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.25, 0.0))
                this.add('complement 0', _complement)
                this.add('complement 1', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.75, 0.0))
                this.add('complement 2', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.50, 0.0))
                this.add('complement 3', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.25, 0.0))
            else
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.25, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.75, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.25, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.75, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.25, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.75, 0.0))
                this.add('complement 0', _complement)
                this.add('complement 1', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.25, 0.0))
                this.add('complement 2', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.50, 0.0))
                this.add('complement 3', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.75, 0.0))
        __CMODEL5__ =>
            // tetradic generates 3 complement colors in a rectangular shape and 3 shades for each.
            [_complement, _lower, _upper] = colex.square(primary)
            [_complement_hue, _complement_saturation, _complement_lightness] = colex.rgb_to_hsl(color.r(_complement), color.g(_complement), color.b(_complement))
            [_lower_hue, _lower_saturation, _lower_lightness] = colex.rgb_to_hsl(color.r(_lower), color.g(_lower), color.b(_lower))
            [_upper_hue, _upper_saturation, _upper_lightness] = colex.rgb_to_hsl(color.r(_upper), color.g(_upper), color.b(_upper))
            // generate shades to dark or light
            if _primary_lightness >= 50.0
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.75, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness * 0.25, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.75, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness * 0.25, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.75, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness * 0.25, 0.0))
                this.add('complement 0', _complement)
                this.add('complement 1', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.75, 0.0))
                this.add('complement 2', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.50, 0.0))
                this.add('complement 3', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness * 0.25, 0.0))
            else
                this.add('primary 1', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.25, 0.0))
                this.add('primary 2', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.50, 0.0))
                this.add('primary 3', colex.hsl(_primary_hue, _primary_saturation, _primary_lightness + (100.0 - _primary_lightness) * 0.75, 0.0))
                this.add('lower 0', _lower)
                this.add('lower 1', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.25, 0.0))
                this.add('lower 2', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.50, 0.0))
                this.add('lower 3', colex.hsl(_lower_hue, _lower_saturation, _lower_lightness + (100.0 - _lower_lightness) * 0.75, 0.0))
                this.add('upper 0', _upper)
                this.add('upper 1', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.25, 0.0))
                this.add('upper 2', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.50, 0.0))
                this.add('upper 3', colex.hsl(_upper_hue, _upper_saturation, _upper_lightness + (100.0 - _upper_lightness) * 0.75, 0.0))
                this.add('complement 0', _complement)
                this.add('complement 1', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.25, 0.0))
                this.add('complement 2', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.50, 0.0))
                this.add('complement 3', colex.hsl(_complement_hue, _complement_saturation, _complement_lightness + (100.0 - _complement_lightness) * 0.75, 0.0))
    this

// }

//#endregion



//#region   -> Preview:

color primary = input.color(#e3f650)
// int imodel = input(1)
string model = input.string(__CMODEL1__, options=[__CMODEL1__, __CMODEL2__, __CMODEL3__, __CMODEL4__, __CMODEL5__])
int imodel = model_index(model)
Theme theme = new()
if barstate.isfirst
    theme.generate_scheme(primary, model(imodel))
    _T = table.new(position.bottom_right, 4, 4)
    _T.cell(0, 0, 'Primary', bgcolor=theme.get_color('primary 0'))
    if imodel > 0
        _T.cell(1, 0, 'Primary 1', bgcolor=theme.get_color('primary 1'))
        _T.cell(2, 0, 'Primary 2', bgcolor=theme.get_color('primary 2'))
        _T.cell(3, 0, 'Primary 3', bgcolor=theme.get_color('primary 3'))
    if imodel > 1
        _T.cell(0, 1, 'lower 0', bgcolor=theme.get_color('lower 0'))
        _T.cell(1, 1, 'lower 1', bgcolor=theme.get_color('lower 1'))
        _T.cell(2, 1, 'lower 2', bgcolor=theme.get_color('lower 2'))
        _T.cell(3, 1, 'lower 3', bgcolor=theme.get_color('lower 3'))
        _T.cell(0, 2, 'upper 0', bgcolor=theme.get_color('upper 0'))
        _T.cell(1, 2, 'upper 1', bgcolor=theme.get_color('upper 1'))
        _T.cell(2, 2, 'upper 2', bgcolor=theme.get_color('upper 2'))
        _T.cell(3, 2, 'upper 3', bgcolor=theme.get_color('upper 3'))
    if imodel > 3
        _T.cell(0, 3, 'complement 0', bgcolor=theme.get_color('complement 0'))
        _T.cell(1, 3, 'complement 1', bgcolor=theme.get_color('complement 1'))
        _T.cell(2, 3, 'complement 2', bgcolor=theme.get_color('complement 2'))
        _T.cell(3, 3, 'complement 3', bgcolor=theme.get_color('complement 3'))

//#endregion
