#include "core/public/plugin.h"

static PF_Err About(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output)
{
    AEGP_SuiteHandler suites(in_data->pica_basicP);

    suites.ANSICallbacksSuite1()->sprintf(out_data->return_msg, "%s v%d.%d\r%s", STR(StrID_Name), MAJOR_VERSION, MINOR_VERSION, STR(StrID_Description));
    return PF_Err_NONE;
}

static PF_Err GlobalSetup(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output)
{
    out_data->my_version = PF_VERSION(MAJOR_VERSION, MINOR_VERSION, BUG_VERSION, STAGE_VERSION, BUILD_VERSION);

    out_data->out_flags = PF_OutFlag_DEEP_COLOR_AWARE;

    return PF_Err_NONE;
}

static PF_Err ParamsSetup(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output)
{
    PF_Err err = PF_Err_NONE;
    PF_ParamDef def;

    AEFX_CLR_STRUCT(def);
    PF_ADD_FLOAT_SLIDERX(STR(StrID_Gain_Param_Name), SKELETON_GAIN_MIN, SKELETON_GAIN_MAX, SKELETON_GAIN_MIN, SKELETON_GAIN_MAX, SKELETON_GAIN_DFLT, PF_Precision_HUNDREDTHS, 0, 0, GAIN_DISK_ID);

    AEFX_CLR_STRUCT(def);
    PF_ADD_COLOR(STR(StrID_Color_Param_Name), PF_HALF_CHAN8, PF_MAX_CHAN8, PF_MAX_CHAN8, COLOR_DISK_ID);

    out_data->num_params = SKELETON_NUM_PARAMS;

    return err;
}

static PF_Err MySimpleGainFunc16(void *refcon, A_long xL, A_long yL, PF_Pixel16 *inP, PF_Pixel16 *outP)
{
    PF_Err err = PF_Err_NONE;

    GainInfo *giP = reinterpret_cast<GainInfo *>(refcon);
    PF_FpLong tempF = 0;

    if (giP)
    {
        tempF = giP->gainF * PF_MAX_CHAN16 / 100.0;
        if (tempF > PF_MAX_CHAN16)
        {
            tempF = PF_MAX_CHAN16;
        };

        outP->alpha = inP->alpha;
        outP->red = MIN((inP->red + (A_u_char)tempF), PF_MAX_CHAN16);
        outP->green = MIN((inP->green + (A_u_char)tempF), PF_MAX_CHAN16);
        outP->blue = MIN((inP->blue + (A_u_char)tempF), PF_MAX_CHAN16);
    }

    return err;
}

static PF_Err MySimpleGainFunc8(void *refcon, A_long xL, A_long yL, PF_Pixel8 *inP, PF_Pixel8 *outP)
{
    PF_Err err = PF_Err_NONE;

    GainInfo *giP = reinterpret_cast<GainInfo *>(refcon);
    PF_FpLong tempF = 0;

    if (giP)
    {
        tempF = giP->gainF * PF_MAX_CHAN8 / 100.0;
        if (tempF > PF_MAX_CHAN8)
        {
            tempF = PF_MAX_CHAN8;
        };

        outP->alpha = inP->alpha;
        outP->red = MIN((inP->red + (A_u_char)tempF), PF_MAX_CHAN8);
        outP->green = MIN((inP->green + (A_u_char)tempF), PF_MAX_CHAN8);
        outP->blue = MIN((inP->blue + (A_u_char)tempF), PF_MAX_CHAN8);
    }

    return err;
}

static PF_Err Render(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output)
{
    PF_Err err = PF_Err_NONE;
    AEGP_SuiteHandler suites(in_data->pica_basicP);

    /*	Put interesting code here. */
    GainInfo giP;
    AEFX_CLR_STRUCT(giP);
    A_long linesL = 0;

    linesL = output->extent_hint.bottom - output->extent_hint.top;
    giP.gainF = params[SKELETON_GAIN]->u.fs_d.value;

    if (PF_WORLD_IS_DEEP(output))
    {
        ERR(suites.Iterate16Suite2()->iterate(in_data,
                                              0,                             // progress base
                                              linesL,                        // progress final
                                              &params[SKELETON_INPUT]->u.ld, // src
                                              NULL,                          // area - null for all pixels
                                              (void *)&giP,                  // refcon - your custom data pointer
                                              MySimpleGainFunc16,            // pixel function pointer
                                              output));
    }
    else
    {
        ERR(suites.Iterate8Suite2()->iterate(in_data,
                                             0,                             // progress base
                                             linesL,                        // progress final
                                             &params[SKELETON_INPUT]->u.ld, // src
                                             NULL,                          // area - null for all pixels
                                             (void *)&giP,                  // refcon - your custom data pointer
                                             MySimpleGainFunc8,             // pixel function pointer
                                             output));
    }

    return err;
}

extern "C" DllExport PF_Err PluginDataEntryFunction2(PF_PluginDataPtr inPtr, PF_PluginDataCB2 inPluginDataCallBackPtr, SPBasicSuite *inSPBasicSuitePtr, const char *inHostName, const char *inHostVersion)
{
    PF_Err result = PF_Err_INVALID_CALLBACK;

    result = PF_REGISTER_EFFECT_EXT2(
        inPtr,
        inPluginDataCallBackPtr,
        "{{name}}",                       // Name
        "{{shortCategory}} {{name}}",     // Match Name
        "{{category}}",                   // Category
        AE_RESERVED_INFO,                 // Reserved Info
        "EffectMain",                     // Entry point
        "https://exaecut.io/no-support"); // support URL

    return result;
}

PF_Err EffectMain(PF_Cmd cmd, PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output, void *extra)
{
    PF_Err err = PF_Err_NONE;

    try
    {
        switch (cmd)
        {
        case PF_Cmd_ABOUT:
            err = About(in_data, out_data, params, output);
            break;

        case PF_Cmd_GLOBAL_SETUP:
            err = GlobalSetup(in_data, out_data, params, output);
            break;

        case PF_Cmd_PARAMS_SETUP:
            err = ParamsSetup(in_data, out_data, params, output);
            break;

        case PF_Cmd_RENDER:
            err = Render(in_data, out_data, params, output);
            break;
        }
    }
    catch (PF_Err &thrown_err)
    {
        err = thrown_err;
    }

    return err;
}
