/***************************************************************************************
Copyright (C) 2016 Maxim L. Grishin  (altmer@arts-union.ru)
This script completely free, released under the zlib license:

This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:

    1.The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.

    2.Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.

    3.This notice may not be removed or altered from any source distribution.
***************************************************************************************/

const int IMF_LINE45 = 0x00001;
const int IMF_LINE26 = 0x00002;
const int IMF_LINE70 = 0x00004;

int topo_i,topo_j,topo_type;
int w;
int h;
int factor;
array<uint> edges;

bool Transp(uint color)
{
    return (color&0x80000000)!=0;
}

int Domen(uint color)
{
    return color&0x8000;
}

int Red(uint color)
{
    return (color&31)<<3;
}

int Green(uint color)
{
    return ((color>>5)&31)<<3;
}

int Blue(uint color)
{
    return ((color>>10)&31)<<3;
}

int AMVRed(uint color)
{
    return ((color>>16)&7)<<5;
}

int AMVGreen(uint color)
{
    return ((color>>19)&7)<<5;
}

int AMVBlue(uint color)
{
    return ((color>>22)&7)<<5;
}

uint makeAMVColor(uint patt, int r, int g, int b, int ar, int ag, int ab)
{
    return  (patt&0x80008000)|(r>>3)|((g>>3)<<5)|((b>>3)<<10)
            |((ar>>5)<<16)|((ag>>5)<<19)|((ab>>5)<<22);
}

uint get_color(int i, int j)
{
    int t_i=i,t_j=j;
    switch(topo_type)
    {
    case 1:
        t_i=(j-topo_j)+topo_i;
        t_j=topo_j-(i-topo_i);
        break;
    case 2:
        t_i=topo_i-(i-topo_i);
        t_j=topo_j-(j-topo_j);
        break;
    case 3:
        t_i=topo_i-(j-topo_j);
        t_j=(i-topo_i)+topo_j;
        break;
    };
    i=t_i;
    j=t_j;

    return src(j,i);
}

void fill_side(int ii, int jj, float x0, float y0, float x1, float y1, bool inc, uint col)
{
    float t_x0=x0,t_y0=y0,t_x1=x1,t_y1=y1;
    uint cy=get_color(ii-1,jj),cx=get_color(ii,jj+1);
    switch(topo_type)
    {
    case 1:
        t_x0=y0; t_x1=y1; t_y0=-x0; t_y1=-x1;
        break;
    case 2:
        t_x0=-x0; t_x1=-x1; t_y0=-y0; t_y1=-y1;
        inc=true;
        break;
    case 3:
        t_x0=-y0; t_y0=x0; t_x1=-y1; t_y1=x1;
        inc=true;
        break;
    };
    x0=t_x0; y0=t_y0; x1=t_x1; y1=t_y1;

    float a=y0-y1;
	float b=x1-x0;
	float c=x0*y1-x1*y0;

    if(Transp(cx))cx=cy;
    if(Transp(cy))cy=cx;

    for(int i=0;i<factor;i++)
    {
        for(int j=0;j<factor;j++)
        {
            float dst= ((-factor/2.0+j+0.5)/(float(factor)))*a+((factor/2.0-i-0.5)/(float(factor)))*b+c;
            if(dst>0.0 || (inc && dst>=0.0))
            {
                int t_i=i,t_j=j;
                switch(topo_type)
                {
                case 1:
                    t_j=i;
                    t_i=factor-j-1;
                    break;
                case 2:
                    t_i=factor-i-1;
                    t_j=factor-j-1;
                    break;
                case 3:
                    t_i=j;
                    t_j=factor-i-1;
                    break;
                };

				float x=(t_j+0.5)/float(factor);
				float y=1.0-(t_i+0.5)/float(factor);
				x/=x+y;
                set(jj*factor+j,ii*factor+i,mix(x/(x+y),cx,cy));
            }
        }
    }
}

int make_patterns(int i, int j)
{
    int rv=0;

    uint32 curr=get_color(i,j);
    uint32 left=get_color(i,j-1);
    uint32 right=get_color(i,j+1);
    uint32 up=get_color(i-1,j);
    uint32 down=get_color(i+1,j);

    if(curr==left && curr==right && curr==up && curr==down)return rv;

    uint32 left_up=get_color(i-1,j-1);
    uint32 left_down=get_color(i+1,j-1);
    uint32 right_up=get_color(i-1,j+1);
    uint32 right_down=get_color(i+1,j+1);

    uint32 right_right_down=get_color(i+1,j+2);
    uint32 up_up=get_color(i-2,j);
    uint32 left_up_up=get_color(i-2,j-1);
    uint32 right_right=get_color(i,j+2);

    if((right_up==right || Transp(curr) || (Transp(right_up) && right_up!=curr)) && right==up && curr!=up)
    {
        if((up_up==up && left_up!=up && left_up_up!=up && right_down==up)
                || (right_down==up))
        {
            rv|=IMF_LINE26;
            fill_side(i,j,0.0,0.5,0.5,-0.5,false,up);
        }
        if((right_right==up && left_down!=up && right_right_down!=up && left_up==up)
                || (left_up==up))
        {
            rv|=IMF_LINE70;
            fill_side(i,j,-0.5,0.5,0.5,0.0,false,right);
        }
        if((rv&(IMF_LINE26|IMF_LINE70))==0)
        {
            rv|=IMF_LINE45;
            fill_side(i,j,0.0,0.5,0.5,0.0,false,right);
        }
    }
    if(left==curr && down==curr && curr!=up && curr!=right && (curr!=right_up || Transp(curr)))
    {
        if(right_down==curr && (up==right || !Transp(right)))
        {
            if(right_right_down==curr && left_up!=curr /*&& isTransp(curr)*/)
            {
                rv|=IMF_LINE70;
                fill_side(i,j,-0.5,0.5,0.5,0.0,false,right);
            }
            else
            {
                rv|=IMF_LINE45;
                fill_side(i,j,0.0,0.5,0.5,0.0,false,right);
            }
        }
        if(left_up==curr && (up==right || !Transp(up)))
        {
            if(left_up_up==curr && right_down!=curr && right==right_down /*&& isTransp(curr)*/)
            {
                rv|=IMF_LINE26;
                fill_side(i,j,0.0,0.5,0.5,-0.5,false,up);
            }
            else
            {
                rv|=IMF_LINE45;
                fill_side(i,j,0.0,0.5,0.5,0.0,false,up);
            }
        }
    }

    if(rv==0 && !Transp(curr) && (left==curr || right_right_down==curr) && right_down==curr &&
            left_up!=curr && right!=curr && right_right!=curr && up!=curr
            && Transp(right)==Transp(up))
    {
        if(left_up==up)
        {
            rv|=IMF_LINE70;
            fill_side(i,j,-0.5,0.5,0.5,0.0,false,right);
        }
        else
        {
            rv|=IMF_LINE45;
            fill_side(i,j,0.0,0.5,0.5,0.0,false,right);
        }
    }

    if(rv==0 && Transp(curr) && curr==down && right!=curr && up!=curr && left!=curr)
    {
        rv|=IMF_LINE45;
        fill_side(i,j,0.0,0.5,0.5,0.0,false,right);
    }

    if(rv==0 && Transp(curr) && curr==left && right!=curr && up!=curr && down!=curr)
    {
        rv|=IMF_LINE45;
        fill_side(i,j,0.0,0.5,0.5,0.0,false,up);
    }

    if(rv==0 && !Transp(curr) && curr==left_up && curr==right_down && right!=curr && up!=curr && Transp(right)==Transp(up))
    {
        rv|=IMF_LINE45;
        fill_side(i,j,0.0,0.5,0.5,0.0,false,up);
    }

    return rv;
}
bool isClear(uint val)
{
	if(val==0)return true;
	return false;
}

bool isClear(uint val, int corner)
{	
	if(corner==0 && (val&0xff)==0 && ((val>>8)&IMF_LINE70)==0 && ((val>>24)&IMF_LINE26)==0)return true;
	if(corner==1 && ((val>>8)&0xff)==0 && ((val>>16)&IMF_LINE70)==0 && (val&IMF_LINE26)==0)return true;
	if(corner==2 && ((val>>16)&0xff)==0 && ((val>>24)&IMF_LINE70)==0 && ((val>>8)&IMF_LINE26)==0)return true;
	if(corner==3 && ((val>>24)&0xff)==0 && (val&IMF_LINE70)==0 && ((val>>16)&IMF_LINE26)==0)return true;
	return false;
}

uint get_edge(int i, int j)
{
    uint rv;
    if(i<0 || i>=h || j<0 || j>=w)
    {
        rv=0;
    }
    else
    {
        rv=edges[i*w+j];
    }
    return rv;
}

void make_filtration(int ii, int jj)
{
    uint curr=get_edge(ii,jj);
    uint right_up=get_edge(ii-1,jj+1);
    uint right=get_edge(ii,jj+1);
    uint up=get_edge(ii-1,jj);

    uint c_col=get_color(ii,jj);
    uint ru_col=get_color(ii-1,jj+1);
    uint r_col=get_color(ii,jj+1);
    uint u_col=get_color(ii-1,jj);

    uint right_down=get_edge(ii+1,jj+1);
    uint down=get_edge(ii+1,jj);

    uint rd_col=get_color(ii+1,jj+1);
    uint d_col=get_color(ii+1,jj);

    uint left_down=get_edge(ii+1,jj-1);
    uint left=get_edge(ii,jj-1);

    uint ld_col=get_color(ii+1,jj-1);
    uint l_col=get_color(ii,jj-1);

    uint left_up=get_edge(ii-1,jj-1);

    uint lu_col=get_color(ii-1,jj-1);

    if(!(Transp(c_col) || Transp(ru_col) || Transp(r_col) || Transp(u_col))
            && isClear(curr,0) && isClear(right_up,2) && isClear(up,1) && isClear(right,3)
            && c_col!=u_col && u_col!=ru_col && ru_col!=r_col && r_col!=c_col
            && Domen(c_col)==Domen(ru_col) && Domen(ru_col)==Domen(r_col) && Domen(r_col)==Domen(u_col))
    {
        for(int i=0;i<factor/2;i++)
        {
            for(int j=factor/2+(factor&1)+i;j<factor;j++)
            {
                float x=(j+0.5-factor*0.5-(i+0.5))/(factor-2.0*(i+0.5));
                float y=(factor*0.5-i-0.5-(factor-j-0.5))/(factor-2.0*(factor-j-0.5));

                set(jj*factor+j,ii*factor+i,mix(x,y,u_col,ru_col,r_col,c_col));
            }
        }
    }
    else if(!(Transp(c_col) || Transp(r_col) || Transp(u_col))
            && ((right_up>>16)&0xff)!=0 && c_col!=r_col && c_col!=u_col)
    {
        fill_side(ii,jj,0.0,0.5,0.5,0.0,false,u_col);
    }


    if(!(Transp(c_col) || Transp(rd_col) || Transp(r_col) || Transp(d_col))
            && isClear(curr,1) && isClear(right,2) && isClear(down,0) && isClear(right_down,3)
            && c_col!=r_col && r_col!=rd_col && rd_col!=d_col && d_col!=c_col
            && Domen(c_col)==Domen(rd_col) && Domen(rd_col)==Domen(r_col) && Domen(r_col)==Domen(d_col))
    {
        for(int i=factor/2+(factor&1);i<factor;i++)
        {
            for(int j=factor-(i-factor/2-(factor&1))-1;j<factor;j++)
            {
                float x=(j+0.5-factor*0.5-(factor-(i+0.5)))/(factor-2.0*((factor-(i+0.5))));
                float y=(factor*1.5-i-0.5-(factor-j-0.5))/(factor-2.0*(factor-j-0.5));

                set(jj*factor+j,ii*factor+i,mix(x,y,c_col,r_col,rd_col,d_col));
            }
        }
    }
    else if(!(Transp(c_col) || Transp(d_col) || Transp(r_col))
            && ((right_down>>24)&0xff)!=0 && c_col!=d_col && c_col!=r_col)
    {
        topo_type=1;
        fill_side(ii,jj,0.0,0.5,0.5,0.0,false,r_col);
    }


    if(!(Transp(c_col) || Transp(d_col) || Transp(ld_col) || Transp(l_col))
            && isClear(curr,2) && isClear(left_down,0) && isClear(down,3) && isClear(left,1)
            && c_col!=d_col && d_col!=ld_col && ld_col!=l_col && l_col!=c_col
            && Domen(c_col)==Domen(d_col) && Domen(d_col)==Domen(ld_col) && Domen(ld_col)==Domen(l_col))
    {
        for(int i=factor/2+(factor&1);i<factor;i++)
        {
            for(int j=0;j<1+i-(factor/2+(factor&1));j++)
            {
                float x=(j+0.5+factor*0.5-(factor-(i+0.5)))/(factor-2.0*((factor-(i+0.5))));
                float y=(factor*1.5-i-0.5-(j+0.5))/(factor-2.0*(j+0.5));

                set(jj*factor+j,ii*factor+i,mix(x,y,l_col,c_col,d_col,ld_col));
            }
        }
    }
    else if(!(Transp(c_col) || Transp(d_col) || Transp(l_col))
            && ((left_down)&0xff)!=0 && c_col!=d_col && c_col!=l_col)
    {
        topo_type=2;
        fill_side(ii,jj,0.0,0.5,0.5,0.0,false,d_col);
    }


    if(!(Transp(c_col) || Transp(l_col) || Transp(lu_col) || Transp(u_col))
            && isClear(curr,3) && isClear(left_up,1) && isClear(up,2) && isClear(left,0)
            && c_col!=l_col && l_col!=lu_col && lu_col!=u_col && u_col!=c_col
            && Domen(c_col)==Domen(l_col) && Domen(l_col)==Domen(lu_col) && Domen(lu_col)==Domen(u_col))
    {
        for(int i=0;i<factor/2;i++)
        {
            for(int j=0;j<factor/2-i;j++)
            {
                float x=(j+0.5+factor*0.5-(i+0.5))/(factor-2.0*((i+0.5)));
                float y=(factor*0.5-i-0.5-(j+0.5))/(factor-2.0*(j+0.5));

                set(jj*factor+j,ii*factor+i,mix(x,y,lu_col,u_col,c_col,l_col));
            }
        }
    }
    else if(!(Transp(c_col) || Transp(u_col) || Transp(l_col))
            && ((left_up>>8)&0xff)!=0 && c_col!=u_col && c_col!=l_col)
    {
        topo_type=3;
        fill_side(ii,jj,0.0,0.5,0.5,0.0,false,l_col);
    }
}

//точка входа в скрипт, она должна быть именно такой и никакой другой!
void filter(int scale, uint ws, uint hs)
{    
	w=ws;
	h=hs;
	factor=scale;
	
	topo_type = 0;
    	
	if(w==1 || h==1) return;
	
	edges.resize(w*h);
	
	for(int i=0;i<h;i++)
    {
        for(int j=0;j<w;j++)
        {
            topo_i=i;
            topo_j=j;
            topo_type=0;
            edges[i*w+j]=make_patterns(i,j); //ru
            topo_type=1;
            edges[i*w+j]|=make_patterns(i,j)<<8; //rd
            topo_type=2;
            edges[i*w+j]|=make_patterns(i,j)<<16; //ld
            topo_type=3;
            edges[i*w+j]|=make_patterns(i,j)<<24; //lu
        }
    }
	
	for(int i=0;i<h;i++)
    {
        for(int j=0;j<w;j++)
        {
            topo_i=i;
            topo_j=j;
            topo_type=0;
            make_filtration(i,j);
        }
    }
	
}










