/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ip.rsh"
#pragma rs_fp_relaxed

static float shadowFilterMap[] = {
    -0.00591f,  0.0001f,
     1.16488f,  0.01668f,
    -0.18027f, -0.06791f,
    -0.12625f,  0.09001f,
     0.15065f, -0.03897f
};

static float poly[] = {
    0.f, 0.f,
    0.f, 0.f,
    0.f
};

static const int ABITS = 4;
static const int HSCALE = 256;
static const int k1=255 << ABITS;
static const int k2=HSCALE << ABITS;

static float fastevalPoly(float *poly,int n, float x){

    float f =x;
    float sum = poly[0]+poly[1]*f;
    int i;
    for (i = 2; i < n; i++) {
        f*=x;
        sum += poly[i]*f;
    }
    return sum;
}

static ushort3 rgb2hsv( float4 rgb)
{
    int iMin,iMax,chroma;

    int ri = rgb.r;
    int gi = rgb.g;
    int bi = rgb.b;
    short rv,rs,rh;

    if (ri > gi) {
        iMax = max (ri, bi);
        iMin = min (gi, bi);
    } else {
        iMax = max (gi, bi);
        iMin = min (ri, bi);
    }

    chroma = iMax - iMin;
    // set value
    rv = (short)( iMax << ABITS);

    // set saturation
    if (rv == 0)
        rs = 0;
    else
        rs = (short)((k1*chroma)/iMax);

    // set hue
    if (rs == 0)
        rh = 0;
    else {
        if ( ri == iMax ) {
            rh  = (short)( (k2*(6*chroma+gi - bi))/(6*chroma));
            if (rh >= k2) rh -= k2;
        } else if (gi  == iMax)
            rh  = (short)( (k2*(2*chroma+bi - ri ))/(6*chroma));
        else // (bi == iMax )
                    rh  = (short)( (k2*(4*chroma+ri - gi ))/(6*chroma));
    }

    ushort3 out;
    out.x = rv;
    out.y = rs;
    out.z = rh;
    return out;
}

static float4 hsv2rgb(ushort3 hsv)
{
    int ABITS = 4;
    int HSCALE = 256;
    int m;
    int H,X,ih,is,iv;
    int k1=255<<ABITS;
    int k2=HSCALE<<ABITS;
    int k3=1<<(ABITS-1);
    int rr=0;
    int rg=0;
    int rb=0;
    short cv = hsv.x;
    short cs = hsv.y;
    short ch = hsv.z;

    // set chroma and min component value m
    //chroma = ( cv * cs )/k1;
    //m = cv - chroma;
    m = ((int)cv*(k1 - (int)cs ))/k1;

    // chroma  == 0 <-> cs == 0 --> m=cv
    if (cs == 0) {
        rb = ( rg = ( rr =( cv >> ABITS) ));
    } else {
        ih=(int)ch;
        is=(int)cs;
        iv=(int)cv;

        H = (6*ih)/k2;
        X = ((iv*is)/k2)*(k2- abs(6*ih- 2*(H>>1)*k2 - k2)) ;

        // removing additional bits --> unit8
        X=( (X+iv*(k1 - is ))/k1 + k3 ) >> ABITS;
        m=m >> ABITS;

        // ( chroma + m ) --> cv ;
        cv=(short) (cv >> ABITS);
        switch (H) {
        case 0:
            rr = cv;
            rg = X;
            rb = m;
            break;
        case 1:
            rr = X;
            rg = cv;
            rb = m;
            break;
        case 2:
            rr = m;
            rg = cv;
            rb = X;
            break;
        case 3:
            rr = m;
            rg = X;
            rb = cv;
            break;
        case 4:
            rr = X;
            rg = m;
            rb = cv;
            break;
        case 5:
            rr = cv;
            rg = m ;
            rb = X;
            break;
        }
    }

    float4 rgb;

    rgb.r =  rr;
    rgb.g =  rg;
    rgb.b =  rb;

    return rgb;
}

void prepareShadows(float scale) {
    float s = (scale>=0) ? scale : scale / 5.f;
    for (int i = 0; i < 5; i++) {
        poly[i] = fastevalPoly(shadowFilterMap+i*2,2 , s);
    }
}

float4 RS_KERNEL shadowsKernel(float4 in) {
    ushort3 hsv = rgb2hsv(in);
    float v = (fastevalPoly(poly, 5, hsv.x * (1.f / 4080.f)) * 4080.f);
    hsv.x = (unsigned short) clamp(v, 0.f, 4080.f);
    return hsv2rgb(hsv);
}
