#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#define READ_INT_HOAX ((((unsigned int)(unsigned char)(hoax[index+3]))<<24)|(((unsigned int)(unsigned char)(hoax[index+2]))<<16)|(((unsigned int)(unsigned char)(hoax[index+1]))<<8)|((unsigned int)(unsigned char)(hoax[index]))),index+=4
#define READ_SHORT_HOAX ((((unsigned short)(unsigned char)(hoax[index+1]))<<8)|(unsigned short)(unsigned char)(hoax[index])),index+=2
#define HOAX_INT_FILL(a) hoax[index+3]=char(a>>24),hoax[index+2]=char(a>>16),hoax[index+1]=char(a>>8),hoax[index]=char(a),index+=4
#define HOAX_SHORT_FILL(a) hoax[index+1]=char(a>>8),hoax[index]=char(a),index+=2
using namespace std;

struct color{
    int r, g, b;
    color(){
        r=0,g=0,b=0;
    };
    color(int r_, int g_, int b_){
        r = r_; g = g_; b = b_;
    };
    color operator = (const color &other){
        r = other.r; g = other.g; b = other.b; return *this;
    };
    const bool operator == (const color &other){
        return (r == other.r && g == other.g && b == other.b);
    };
};

struct bitmap{
    char *hoax;
    ifstream fin;
    ofstream fout;
    short magic;
    int size_bmp, unused, offset_data, header_bytes;
    int width, height;
    short color_planes, color_bpp;
    int compression, size_data;
    int horiz_resolution, vertical_resolution;
    int colors_palette, mean_palette;
    vector<vector<color> > bmp;
    bitmap(){
        return;
    };
    bitmap(const string &file){
        read_bmp(file);
    };
    void read_bmp(const string &file){
        fin.open(file.c_str(), ios_base::binary);
        int index = 0;
        hoax = new char[54];
        fin.read(hoax, 54);
        magic = READ_SHORT_HOAX;
        size_bmp = READ_INT_HOAX;
        unused = READ_INT_HOAX;
        offset_data = READ_INT_HOAX;
        header_bytes = READ_INT_HOAX;
        width = READ_INT_HOAX;
        height = READ_INT_HOAX;
        color_planes = READ_SHORT_HOAX;
        color_bpp = READ_SHORT_HOAX;
        compression = READ_INT_HOAX;
        size_data = READ_INT_HOAX;
        horiz_resolution = READ_INT_HOAX;
        vertical_resolution = READ_INT_HOAX;
        colors_palette = READ_INT_HOAX;
        mean_palette = READ_INT_HOAX;
        bmp = vector<vector<color> >(height, vector<color>(width));
        delete hoax;
        hoax = new char[height*(width*3+width%4)];
        fin.read(hoax, height*(width*3+width%4));
        index = 0;
        for(int i = 0; i < height; i++){
            vector<color> row;
            for(int j = 0; j < width; j++){
                bmp[i][j] = color((int)(unsigned char)hoax[index+2],(int)(unsigned char)hoax[index+1],(int)(unsigned char)hoax[index]);
                index += 3;
            }
            index += width%4;
        }
        delete hoax;
        fin.close();
    };
    void write_bmp(const string &file){
        fout.open(file.c_str(), ios_base::binary);
        hoax = new char[54];
        int index = 0;
        HOAX_SHORT_FILL(magic);
        HOAX_INT_FILL(size_bmp);
        HOAX_INT_FILL(unused);
        HOAX_INT_FILL(offset_data);
        HOAX_INT_FILL(header_bytes);
        HOAX_INT_FILL(width);
        HOAX_INT_FILL(height);
        HOAX_SHORT_FILL(color_planes);
        HOAX_SHORT_FILL(color_bpp);
        HOAX_INT_FILL(compression);
        HOAX_INT_FILL(size_data);
        HOAX_INT_FILL(horiz_resolution);
        HOAX_INT_FILL(vertical_resolution);
        HOAX_INT_FILL(colors_palette);
        HOAX_INT_FILL(mean_palette);
        fout.write(hoax, 54);
        delete hoax;
        hoax = new char[height*(width*3+width%4)];
        index = 0;
        for(int i = 0; i < height; i++){
            for(int j = 0; j < width; j++){
                hoax[index] = (unsigned char)bmp[i][j].b;
                hoax[index+1] = (unsigned char)bmp[i][j].g;
                hoax[index+2] = (unsigned char)bmp[i][j].r;
                index += 3;
            }
            index += width % 4;
        }
        fout.write(hoax, index);
        fout.close();
        delete hoax;
    };
    bitmap* operator = (const bitmap &other){
        magic = other.magic;
        size_bmp = other.size_bmp;
        unused = other.unused;
        offset_data = other.offset_data;
        header_bytes = other.header_bytes;
        width = other.width;
        height = other.height;
        color_planes = other.color_planes;
        color_bpp = other.color_bpp;
        compression = other.compression;
        size_data = other.size_data;
        horiz_resolution = other.horiz_resolution;
        vertical_resolution = other.vertical_resolution;
        colors_palette = other.colors_palette;
        mean_palette = other.mean_palette;
        bmp = other.bmp;
        return this;
    };
    bitmap(const bitmap &other){
        *this = other;
    };
    bitmap(const vector<vector<color> > mapa){
        magic = 19778;
        width = mapa[0].size();
        height = mapa.size();
        size_data = height*(width*3+width%4);
        size_bmp = 54 + size_data;
        unused = 0;
        offset_data = 54;
        header_bytes = 40;
        color_planes = 1;
        color_bpp = 24;
        compression = 0;
        horiz_resolution = 2835;
        vertical_resolution = 2835;
        colors_palette = 0;
        mean_palette = 0;
        bmp = mapa;
    };
};

#define ENT 200
#define ax(x) ((x)>=0?(((x)<foto.height)?(x):foto.height-1):0)
#define ay(x) ((x)>=0?(((x)<foto.width)?(x):foto.width-1):0)
#define average_brillo(x, y) (brillantidad(x, y))/(puntidad(x,y))
#define average_color(x, y) (rojosismo(x,y))/(puntidad(x,y))
#define rojosismo(x,y) (c_color[ax(x-ENT)][ay(y-ENT)]-c_color[ax(x+ENT)][ay(y-ENT)]-c_color[ax(x-ENT)][ay(y+ENT)]+c_color[ax(x+ENT)][ay(y+ENT)])
#define brillantidad(x,y) (c_brillo[ax(x-ENT)][ay(y-ENT)]-c_brillo[ax(x+ENT)][ay(y-ENT)]-c_brillo[ax(x-ENT)][ay(y+ENT)]+c_brillo[ax(x+ENT)][ay(y+ENT)])
#define puntidad(x,y) (puntos[ax(x-ENT)][ay(y-ENT)]-puntos[ax(x+ENT)][ay(y-ENT)]-puntos[ax(x-ENT)][ay(y+ENT)]+puntos[ax(x+ENT)][ay(y+ENT)])

int rojosidad(color a){
    return (a.r-max(a.b, a.g));
};

int brillo(color a){
    return (299*a.r + 587*a.g + 114*a.b)/1000;
};

int saturation(color a){
    int ma=max(a.r,max(a.g,a.b)) , mi=min(a.r,min(a.g,a.b));
    if(ma==mi)
        return 0;
    if((ma+mi)/2<128)
        return (int)255*(ma-mi)/(ma+mi);
    return (int)255*(ma-mi)/(510-ma-mi);
}

int main(int argc, char** argv){
    string filename = argv[1];
    bitmap foto(filename);
    bitmap salida(vector<vector<color> >(foto.height, vector<color>(foto.width, color(0,0,0))));

    vector<vector<int> > c_brillo(foto.height+1, vector<int>(foto.width, 0));
    vector<vector<int> > c_color(foto.height+1, vector<int>(foto.width, 0));
    vector<vector<int> > puntos(foto.height+1, vector<int>(foto.width, 0));
    //PRECALCULO
    for(int i = foto.height-1; i >= 0; i--){
        int sum_brillo = 0;
        int sum_color = 0;
        int c = 0;
        for(int j = foto.width-1; j >= 0; j--){
            c++;
            sum_brillo += brillo(foto.bmp[i][j]);
            sum_color += rojosidad(foto.bmp[i][j]);
            puntos[i][j] = c+puntos[i+1][j];
            c_brillo[i][j] = c_brillo[i+1][j]+sum_brillo;
            c_color[i][j] = c_color[i+1][j]+sum_color;
        }
    }
    // MAIN CICLE
    int mean_x = 0, mean_y = 0, points = 0;
    for(int i = 0; i < foto.height; i++)
        for(int j = 0; j < foto.width; j++)
            if (rojosidad(foto.bmp[i][j]) > 4)
                if (saturation(foto.bmp[i][j]) > 200)
                    if (brillo(foto.bmp[i][j]) > average_brillo(i,j)){
                        mean_x += i;
                        mean_y += j;
                        points++;
                        salida.bmp[i][j] = color(255,255,255);
                    }

    ofstream fout("c:\\camara\\punto.txt");
    if (!points)
        fout << "0 0\n";
    else
        fout << mean_y/points << ' ' << mean_x/points << endl;
    fout.close();
    salida.write_bmp(filename + "s.bmp");
    return 0;
}

