#! /usr/bin/env python

import os
import sys
import random

def write_bmp_src(type, nx, ny):
    if type == 1: #cpp
        src_txt = ('int writeBMP(const char *filename, char *data)\n'
                 + '{\n'
                 + 'struct bmpHeader\n'
                 + '{\n'
                 + '    unsigned short MagicWord;\n'
                 + '    unsigned int fileSize;\n'
                 + '    unsigned int noIdea;\n'
                 + '    unsigned int startOfTheBitmap;\n'
                 + '    unsigned int sizeOfTheHeader;\n'
                 + '    unsigned int width;\n'
                 + '    unsigned int height;\n'
                 + '    unsigned short planes;\n'
                 + '    unsigned short bitsPerPixel;\n'
                 + '    unsigned int compression;\n'
                 + '    unsigned int compressedSize;\n'
                 + '    unsigned int horizontalResolution;\n'
                 + '    unsigned int verticalResolution;\n'
                 + '    unsigned int numberOfColorsInPalette;\n'
                 + '    unsigned int importantColors;\n'
                 + '} __attribute__ ((packed));\n'
                 + '    bmpHeader h;\n'
                 + '    h.MagicWord = 19778;\n'
                 + '    h.fileSize = 54 + {0};\n'.format(nx*ny*3)
                 + '    h.noIdea =0;\n'
                 + '    h.startOfTheBitmap = 54;\n'
                 + '    h.sizeOfTheHeader = 40;\n'
                 + '    h.width = {0};\n'.format(nx)
                 + '    h.height = {0};\n'.format(ny)
                 + '    h.planes =1;\n'
                 + '    h.bitsPerPixel = 24;\n'
                 + '    h.compression = 0;\n'
                 + '    h.compressedSize = {0};\n'.format(nx*ny*3)
                 + '    h.horizontalResolution = {0};\n'.format(2835)
                 + '    h.verticalResolution = {0};\n'.format(2835)
                 + '    h.numberOfColorsInPalette = 0;\n'
                 + '    h.importantColors = 0;\n'
                 + '    ofstream output;\n'
                 + '    output.open(filename);\n'
                 + '    output.write((char*)(&h),54);\n'
                 + '    output.write(data,h.compressedSize);\n'
                 + '    output.close();\n'
                 + '    return 0;\n}\n')
    return src_txt

class bmp_fractal:
    def __init__(self, name, fractal_type, color_type, dimx, dimy):
        self.name = name
        self.fractal_type = fractal_type
        self.color_type = color_type
        self.nx = dimx
        self.ny = dimy
        self.compiled = False
        return None
    def create_exec(self):
        src_file = open(self.name + '.cpp', 'w')
        src_file.write('#include <iostream>\n'
                     + '#include <fstream>\n'
                     + '#include <cstdlib>\n'
                     + '#include <cmath>\n'
                     + 'using namespace std;\n'
                     + write_bmp_src(1, self.nx, self.ny)
                     + 'double get_julia_iterations(double x0, double y0, double cx, double cy, int max_iterations)\n'
                     + '{\n'
                     + 'int i;\n'
                     + 'double x = x0;\n'
                     + 'double y = y0;\n'
                     + 'double xtmp = x;\n'
                     + 'for (i=0; i < max_iterations; i++)\n'
                     + '{\n'
                     + 'if ((x*x+y*y)>4) break;\n'
                     + 'xtmp = ' + self.formulax() + ';\n'
                     + 'y = '    + self.formulay() + ';\n'
                     + 'x = xtmp;\n'
                     + '}\n'
                     + 'return i/((double)max_iterations);\n'
                     + '}\n'
                     + 'int main(int argc, char *argv[])\n'
                     + '{\n'
                     + 'unsigned char *map, *rgb;\n'
                     + 'int i, j, max_iterations, color_scheme;\n'
                     + 'double x, y, x0, y0, x1, y1, cx, cy, point_level;\n'
                     + '\n'
                     + 'if (argc != 9)\n'
                     + '{\n'
                     + 'cout << "\\nI want my params!\\n";\n'
                     + 'return 0;\n'
                     + '}\n'
                     + 'x0 = atof(argv[1]);\n'
                     + 'x1 = atof(argv[2]);\n'
                     + 'y0 = atof(argv[3]);\n'
                     + 'y1 = atof(argv[4]);\n'
                     + 'cx = atof(argv[5]);\n'
                     + 'cy = atof(argv[6]);\n'
                     + 'max_iterations = atoi(argv[7]);\n'
                     + 'map = (unsigned char *) malloc({0});\n'.format(self.nx*self.ny*3)
                     + 'for (i=0;i<{0};i++)\n'.format(self.nx)
                     + '{\n'
                     + 'x = x0 + i*(x1-x0)/{0};\n'.format(self.nx)
                     + 'for (j=0;j<{0};j++)\n'.format(self.ny)
                     + '{\n'
                     + 'y = y0 + j*(y1-y0)/{0};\n'.format(self.ny)
                     + 'point_level = get_julia_iterations(x,y,cx,cy,max_iterations);\n'
                     + 'rgb = map + (i+j*{0})*3;\n'.format(self.nx)
                     + self.color_scheme()
                     + '}\n}\n'
                     + 'writeBMP(argv[8],(char*)map);\n'
                     + 'free(map);\n'
                     + 'return 0;\n}\n')
        src_file.close()
        os.system('g++ -O3 -mtune=native -lm ' + self.name + '.cpp -o ' + self.name)
        self.compiled = True
        return None
    def formulax(self):
        if self.fractal_type == 0:
            return 'x0 + x*x - y*y'
        elif self.fractal_type == 1:
            return 'cx + x*x - y*y'
        elif self.fractal_type == -1:
            return 'cx + x'
    def formulay(self):
        if self.fractal_type == 0:
            return 'y0 + 2*x*y'
        elif self.fractal_type == 1:
            return 'cy + 2*x*y'
        elif self.fractal_type == -1:
            return 'cy'
    def color_scheme(self):
        if self.color_type == 0:
            compute_colors = ('rgb[0] = sqrt(point_level)*255;\n'
                            + 'rgb[1] = point_level*255;\n'
                            + 'rgb[2] = point_level*point_level*255;\n')
        elif self.color_type == 1:
            compute_colors = ('if (point_level<.4)\n'
                            + '{\n'
                            + '    rgb[0] = 637.5*point_level;\n'
                            + '}\n'
                            + 'else if (point_level<.8)\n'
                            + '{\n'
                            + '    rgb[0] = 255;\n'
                            + '    rgb[1] = 637.5*(point_level-.4);\n'
                            + '}\n'
                            + 'else\n'
                            + '{\n'
                            + '    rgb[0] = 255;\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[2] = 1275*(point_level-.8);\n'
                            + '}\n')
        elif self.color_type == 2:
            compute_colors = ('if (point_level<.4)\n'
                            + '{\n'
                            + '    rgb[2] = 637.5*point_level;\n'
                            + '}\n'
                            + 'else if (point_level<.8)\n'
                            + '{\n'
                            + '    rgb[2] = 255;\n'
                            + '    rgb[1] = 637.5*(point_level-.4);\n'
                            + '}\n'
                            + 'else\n'
                            + '{\n'
                            + '    rgb[2] = 255;\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[0] = 1275*(point_level-.8);\n'
                            + '}\n')
        elif self.color_type == 3:
            compute_colors = ('if (point_level<.2)\n'
                            + '{\n'
                            + '    rgb[2] = 1275*point_level;\n'
                            + '}\n'
                            + 'else if (point_level<.4)\n'
                            + '{\n'
                            + '    rgb[2] = 255;\n'
                            + '    rgb[1] = 1275*(point_level-.2);\n'
                            + '}\n'
                            + 'else if (point_level<.5)\n'
                            + '{\n'
                            + '    rgb[2] = 255;\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[0] = 2550*(point_level-.4);\n'
                            + '}\n'
                            + 'else if (point_level<.6)\n'
                            + '{\n'
                            + '    rgb[2] = 2550*(.6-point_level);\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[0] = 255;\n'
                            + '}\n'
                            + 'else if (point_level<.8)\n'
                            + '{\n'
                            + '    rgb[1] = 1275*(.8-point_level);\n'
                            + '    rgb[0] = 255;\n'
                            + '}\n'
                            + 'else\n'
                            + '{\n'
                            + '    rgb[0] = 1275*(1.-point_level);\n'
                            + '}\n')
        elif self.color_type == 4:
            compute_colors = ('if (point_level<.2)\n'
                            + '{\n'
                            + '    rgb[0] = 1275*point_level;\n'
                            + '}\n'
                            + 'else if (point_level<.4)\n'
                            + '{\n'
                            + '    rgb[0] = 255;\n'
                            + '    rgb[1] = 1275*(point_level-.2);\n'
                            + '}\n'
                            + 'else if (point_level<.5)\n'
                            + '{\n'
                            + '    rgb[0] = 255;\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[2] = 2550*(point_level-.4);\n'
                            + '}\n'
                            + 'else if (point_level<.6)\n'
                            + '{\n'
                            + '    rgb[0] = 2550*(.6-point_level);\n'
                            + '    rgb[1] = 255;\n'
                            + '    rgb[2] = 255;\n'
                            + '}\n'
                            + 'else if (point_level<.8)\n'
                            + '{\n'
                            + '    rgb[1] = 1275*(.8-point_level);\n'
                            + '    rgb[2] = 255;\n'
                            + '}\n'
                            + 'else\n'
                            + '{\n'
                            + '    rgb[2] = 1275*(1.-point_level);\n'
                            + '}\n')
        elif self.color_type == 5: # black - red - yellow - green - cyan - blue - purple - white
            fixed_pt =                     [.2,   .4,      .5,     .55,    .65,    .85]
            fixed_cl = [[  0,   0,   0],
                        [255,   0,   0],
                        [255, 255,   0],
                        [  0, 255,   0],
                        [  0, 255, 255],
                        [  0,   0, 255],
                        [255,   0, 255],
                        [255, 255, 255]]
            compute_colors = ('if (point_level<{0})\n'.format(fixed_pt[0])
                            + '{\n'
                            + 'rgb[2] = {0}*point_level;\n'.format((fixed_cl[1][0]-fixed_cl[0][0])/fixed_pt[0])
                            + 'rgb[1] = {0}*point_level;\n'.format((fixed_cl[1][1]-fixed_cl[0][1])/fixed_pt[0])
                            + 'rgb[0] = {0}*point_level;\n'.format((fixed_cl[1][2]-fixed_cl[0][2])/fixed_pt[0])
                            + '}\n')
            for counter in range(1,len(fixed_pt)):
                compute_colors += ('else if (point_level<{0})\n'.format(fixed_pt[counter]) + '{\n')
                for col_counter in range(3):
                    if fixed_cl[counter+1][col_counter] == fixed_cl[counter][col_counter]:
                        compute_colors += 'rgb[{0}] = {1};\n'.format(2-col_counter,fixed_cl[counter][col_counter])
                    elif (fixed_cl[counter][col_counter] == 0):
                        compute_colors += 'rgb[{0}] = {1}*(point_level-{2});\n'.format(2-col_counter,
                                255.0/(fixed_pt[counter]-fixed_pt[counter-1]),fixed_pt[counter-1])
                    elif (fixed_cl[counter][col_counter] == 255):
                        compute_colors += 'rgb[{0}] = {1}*({2}-point_level);\n'.format(2-col_counter,
                                255.0/(fixed_pt[counter]-fixed_pt[counter-1]),fixed_pt[counter])
                compute_colors += '}\n'
            counter = len(fixed_pt)
            compute_colors += 'else\n{\n'
            for col_counter in range(3):
                if fixed_cl[counter+1][col_counter] == fixed_cl[counter][col_counter]:
                    compute_colors += 'rgb[{0}] = {1};\n'.format(2-col_counter,fixed_cl[counter][col_counter])
                elif (fixed_cl[counter][col_counter] == 0):
                    compute_colors += 'rgb[{0}] = {1}*(point_level-{2});\n'.format(2-col_counter,
                            255.0/(1.0-fixed_pt[counter-1]),fixed_pt[counter-1])
                elif (fixed_cl[counter][col_counter] == 255):
                    compute_colors += 'rgb[{0}] = {1}*({2}-point_level);\n'.format(2-col_counter,
                            255.0/(1.0-fixed_pt[counter-1]),1.0)
            compute_colors += '}\n'
        return compute_colors
    def generate_bmp(self, filename, xbound, ybound, max_iterations = 10, c = [-1.4,.0]):
        if self.compiled:
            os.system('./' + self.name
                    + ' {0} {1} {2} {3} {4} {5} {6} {7}'.format(xbound[0], xbound[1], ybound[0], ybound[1],
                                                                c[0], c[1], max_iterations, filename + '.bmp'))
        return None

def mandel_zoom(filename, c, nx, ny, level0, dimrate, levelrate, nlevels):
    mf = bmp_fractal(filename, 0, 5, nx, ny)
    mf.create_exec()
    mf.generate_bmp(filename + '_0', [-2.,2.], [-2., 2.], level0)
    for level in range(1, nlevels):
        mf.generate_bmp(filename + '_{0}'.format(level),
                       [-2*dimrate**level + c[0],2*dimrate**level+c[0]],
                       [-2*dimrate**level + c[1],2*dimrate**level+c[1]], int(level0*levelrate**level))
    return None

def background_16x9(base_n, base_d, iter):
    bf = bmp_fractal('tst', 1, 5, 16*base_n, 9*base_n)
    bf.create_exec()
    bf.generate_bmp('tst', [-16*base_d,16*base_d], [-9*base_d, 9*base_d], iter, [-.7868377, .147479790003])
    return None

def main():
    background_16x9(200, .1, 120)
#    mandel_zoom('m', [-1.4142, .0], 500, 500, 40, .1, 1.25, 15)
    return None

import cProfile
import pstats

if __name__ == '__main__':
    cProfile.run('main()', 'profile_data')
    p = pstats.Stats('profile_data')
    p.sort_stats('cumulative').print_stats(10)
    p.sort_stats('time').print_stats(10)

