package com.codingame.game.engine;

import java.util.Random;

public class Terrain {
    private Engine engine;
    public char[][] type;
    public int hometype;
    public String[][] texture;

    public Random rng;
    public String biome;
    public double bridgePbb;
    public double bigTreePbb;
    public double plainDecPbb;

    public Terrain(Engine engine) {
        this.engine = engine;
    }

    /* Input types -> terrain types
    . ->  .b = plain passable terrain with plainDecPbb random features, can go to bridges with bridgePbb if proper placement regarding water
    , -> .b = random 'road' tiles
    ; -> . = random flower
    t -> t = random tree, with bigTreePbb big one if fits
    T -> t = always standard small tree
    ... todo way more
     */
    public boolean isOpen(int x, int y) { return ".,;'hHb".indexOf(type[x][y]) != -1; }

    public void computeAll() {

        //for (int y=0; y < engine.height; y++)  for (int x=0; x < engine.width; x++)  if (type[x][y] == '@') type[x][y] = 'w';

        computeOther();
        computeHome();
        computeForest();
        computeBridges();
        computePlane();
        computeTrees();
        computeMountains();

        computeWater();
    }

    private void computeHome() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || type[x][y] != 'H') continue;
            switch (hometype) {
                case 0: texture[x][y] = "h"+hometype; return;
                case 1: texture[x][y] = "h"+hometype+"0";  texture[x][y-1] = "h"+hometype+"1"; type[x][y-1]='h';return;
            }

        }}
    }

    private void computeOther() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == '8' || type[x][y] == '9')) continue;
            if (type[x][y] == '8') texture[x][y] = "8"+biome.charAt(0);
            if (type[x][y] == '9') texture[x][y] = "8s";
            type[x][y] = '8';
        }}
    }

    private void computePlane() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == '.' || type[x][y] == ',' || type[x][y] == ';' || type[x][y] == '\'')) continue; // todo g
            if (type[x][y] == ',')  {type[x][y] = '.'; texture[x][y] = "g"+(2+rng.nextInt(2)); continue;} // roads
            if (type[x][y] == ';')  {type[x][y] = '.'; texture[x][y] = "g"+rng.nextInt(2); continue;} // flowers
            if (type[x][y] == '\'') {//type[x][y] = '.';  // dirt+grass
                if (test4(x, y, "''''")) texture[x][y] = "gx"; else texture[x][y] = "gy";
                continue;
            }

            if (rng.nextDouble() > plainDecPbb) {texture[x][y] = "g"; continue; }
            texture[x][y] = "g"+rng.nextInt(7); // for grass
            if (biome.equals("snow")) texture[x][y] = "g"+rng.nextInt(4);
        }}
    }

    private void computeForestPre() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) {if (texture[x][y] != null || !(type[x][y] == 'f')) continue;
            int r = rng.nextInt(3);
            if (test4(x, y, "FFFF")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}

            if (test4(x, y, "fFFF")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}
            if (test4(x, y, "FfFF")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}
            if (test4(x, y, "FFfF")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}
            if (test4(x, y, "FFFf")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}

            if (test4(x, y, "FffF")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}
            if (test4(x, y, "fFFf")) {type[x][y] = '&'; texture[x][y] = "fz"+r; continue;}
        }}
    }
    private void computeForest() {
        computeForestPre(); computeForestPre(); computeForestPre();

        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == 'f')) continue;
            char txt = type[x][y];
            type[x][y] = 'f';


            if (test8(x, y, "Ffffffff")) {texture[x][y] = txt+"8"; continue;}
            if (test8(x, y, "ffFfffff")) {texture[x][y] = txt+"9"; continue;}
            if (test8(x, y, "fffffFff")) {texture[x][y] = txt+"a"; continue;}
            if (test8(x, y, "fffffffF")) {texture[x][y] = txt+"b"; continue;}

            if (test8(x, y, "FffffffF")) {texture[x][y] = txt+"c"; continue;}
            if (test8(x, y, "ffFffFff")) {texture[x][y] = txt+"d"; continue;}


            if (test4(x, y, "ffff")) {texture[x][y] = txt+""; continue;}

            if (test4(x, y, "Ffff")) {texture[x][y] = txt+"0"; continue;}
            if (test4(x, y, "fFff")) {texture[x][y] = txt+"1"; continue;}
            if (test4(x, y, "ffFf")) {texture[x][y] = txt+"2"; continue;}
            if (test4(x, y, "fffF")) {texture[x][y] = txt+"3"; continue;}

            if (test4(x, y, "FFff")) {texture[x][y] = txt+"4"; continue;}
            if (test4(x, y, "FfFf")) {texture[x][y] = txt+"5"; continue;}
            if (test4(x, y, "fFfF")) {texture[x][y] = txt+"6"; continue;}
            if (test4(x, y, "ffFF")) {texture[x][y] = txt+"7"; continue;}

//            if (test4(x, y, "fFFF")) {texture[x][y] = txt+"x"; continue;}
//            if (test4(x, y, "FFFf")) {texture[x][y] = txt+"y"; continue;}

        }}
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) {if (texture[x][y] != null || !(type[x][y] == '&')) continue;
            type[x][y] = 'f';
        }}
    }

    private void computeTrees() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == 't' || type[x][y]=='T' || type[x][y]=='>')) continue;
            if (type[x][y]=='T') {type[x][y]='t'; texture[x][y] = "t0"; continue;}
            if (type[x][y]=='>') {type[x][y]='t'; texture[x][y] = "t3"; continue;}
            if (testSquare(x, y, 't') && rng.nextDouble()<=bigTreePbb) {texture[x][y]="t6";texture[x+1][y]="t7";texture[x][y+1]="t8";texture[x+1][y+1]="t9"; continue;}
            texture[x][y] = "t"+rng.nextInt(5);
        }}
    }

    private void computeMountains() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == 'm' || type[x][y]=='M')) continue;
            if (type[x][y]=='M') {type[x][y]='m'; texture[x][y] = "m0"; continue;}
            texture[x][y] = "m"+rng.nextInt(2);
        }}
    }

    private void computeBridges() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null ||!( type[x][y] == '.' || type[x][y] == ',')) continue;
            if (rng.nextDouble() > bridgePbb) continue;

            if (test4(x, y, "BWWB")) {texture[x][y] = "b0"; type[x][y]='b'; continue;}
            if (test4(x, y, "WBBW")) {texture[x][y] = "b1"; type[x][y]='b'; continue;}
        }}
    }

    private void computeWater() {
        for (int y=0; y < engine.height; y++) { for (int x=0; x < engine.width; x++) { if (texture[x][y] != null || !(type[x][y] == 'w' || type[x][y] == 'r')) continue;
            char txt = type[x][y];
            type[x][y] = 'w';

            if (test4(x, y, "WWWW")) {texture[x][y] = txt+"0"; continue;}

            if (test4(x, y, "WWWw")) {texture[x][y] = txt+"1"; continue;}
            if (test4(x, y, "WWwW")) {texture[x][y] = txt+"2"; continue;}
            if (test4(x, y, "WwWW")) {texture[x][y] = txt+"3"; continue;}
            if (test4(x, y, "wWWW")) {texture[x][y] = txt+"4"; continue;}

            if (test4(x, y, "wWWw")) {texture[x][y] = txt+"5"; continue;}
            if (test4(x, y, "WwwW")) {texture[x][y] = txt+"6"; continue;}

            if (test4(x, y, "WWww")) {texture[x][y] = txt+"7"; continue;}
            if (test4(x, y, "WwWw")) {texture[x][y] = txt+"8"; continue;}
            if (test4(x, y, "wwWW")) {texture[x][y] = txt+"9"; continue;}
            if (test4(x, y, "wWwW")) {texture[x][y] = txt+"a"; continue;}

            if (test4(x, y, "Wwww")) {texture[x][y] = txt+"b"; continue;}
            if (test4(x, y, "wWww")) {texture[x][y] = txt+"c"; continue;}
            if (test4(x, y, "wwWw")) {texture[x][y] = txt+"d"; continue;}
            if (test4(x, y, "wwwW")) {texture[x][y] = txt+"e"; continue;}

            if (test4(x, y, "wwww")) {texture[x][y] = txt+""; continue;}

            //texture[x][y] = "null"; // System.out.println(String.format("w1: %d,%d", x, y));
        }}
    }


    private boolean test4(int x, int y, String tpattern) { // NWES
        return test(x, y, 0, -1, tpattern.charAt(0))  && test(x, y, -1, 0, tpattern.charAt(1)) && test(x, y, 1, 0, tpattern.charAt(2)) && test(x, y, 0, 1, tpattern.charAt(3));
    }

    private boolean test8(int x, int y, String tpattern) { //
        return     test(x, y, -1, -1, tpattern.charAt(0)) && test(x, y, 0, -1, tpattern.charAt(1)) && test(x, y, 1, -1, tpattern.charAt(2))
                && test(x, y, -1, 0, tpattern.charAt(3))                                                   && test(x, y, 1, 0, tpattern.charAt(4))
                && test(x, y, -1, 1, tpattern.charAt(5))  && test(x, y, 0, 1, tpattern.charAt(6))  && test(x, y, 1, 1, tpattern.charAt(7));
    }


    private boolean testSquare(int x, int y, char t) {
        if (x == engine.width-1 || y == engine.height-1) return false;
        return testNTexture(x, y, 1, 0, t)  && testNTexture(x, y, 0, 1, t) && testNTexture(x, y, 1, 1, t);
    }

    private boolean test(int x, int y, String tpattern) {
        return test(x, y, -1, -1, tpattern.charAt(0)) || test(x, y, 0, -1, tpattern.charAt(1)) || test(x, y, 1, -1, tpattern.charAt(2)) || test(x, y, -1, 0, tpattern.charAt(3)) || test(x, y, 0, 0, tpattern.charAt(4)) || test(x, y, 1, 0, tpattern.charAt(5)) || test(x, y, -1, 1, tpattern.charAt(6)) || test(x, y, 0, 1, tpattern.charAt(7)) || test(x, y, 1, 1, tpattern.charAt(8)) ;
    }

    private boolean testNTexture(int x, int y, int dx, int dy, char p) {
        if (texture[x+dx][y+dy] != null) return false;
        return test(x, y, dx, dy, p);
    }

    private boolean test(int x, int y, int dx, int dy, char p) {
        if (p=='?') return true;
        if (x+dx<0 || x+dx==engine.width || y+dy<0 || y+dy==engine.height) return true;
        if (p == 'w') return type[x+dx][y+dy] == 'w' || type[x+dx][y+dy] == 'b' || type[x+dx][y+dy] == 'r';
        if (p == 'W') return type[x+dx][y+dy] != 'w' && type[x+dx][y+dy] != 'b' && type[x+dx][y+dy] != 'r';
        if (p == 'B') return type[x+dx][y+dy] == 'w' && type[x+dx][y+dy] != 'b';
        if (p == 'f') return type[x+dx][y+dy] == 'f';
        if (p == 'F') return type[x+dx][y+dy] != 'f';

        // todo other cases
        return type[x+dx][y+dy] == p;
    }
}
