#include <AsynchronCall.h>
#include <Bild.h>
#include <Bildschirm.h>
#include <Fenster.h>
#include <Globals.h>
#include <iostream>
#include <RenderThread.h>
#include <string.h>

#include "FactorizeNoise.h"
#include "FastNoiseLite.h"
#include "FastNoiseWrapper.h"
#include "MultiplyNoise.h"
#include "NoiseCombiner.h"
#include "RandNoise.h"
#include "ShapedNoise.h"

using namespace Framework;

WFenster* window;
Bild* img;
Vec3<int> position(0, 0, 0);
float zoom = 1;
bool exitF = 0;
Noise* wrapper;
float border = 0.5;
float border2 = -1;
bool showValue = 1;

class A
{
    virtual void f(){};
};

class B : public A
{
    virtual void f() override{};
};

class C : public B
{
    virtual void f() override{};
};

void updateView()
{
    Vec3<int> minP
        = position
        - (Vec3<int>)Vec3<float>(
              (float)img->getBreite() / 2.f, (float)img->getHeight() / 2.f, 0.f)
              / zoom;
    Vec3<int> maxP
        = position
        + (Vec3<int>)Vec3<float>(
              (float)img->getBreite() / 2.f, (float)img->getHeight() / 2.f, 0.f)
              / zoom;
    int counter = 0;
    double min = INFINITY;
    double max = -INFINITY;
    for (int i = 0; i < img->getBreite(); i++)
    {
        for (int j = 0; j < img->getHeight(); j++)
        {
            Vec3<float> pos(i, j, 0);
            pos -= Vec3<int>(img->getBreite() / 2, img->getHeight() / 2, 0);
            pos /= zoom;
            pos += position;
            double noise = wrapper->getNoise(pos.x, pos.y, pos.z);
            if (noise > max)
            {
                max = noise;
            }
            if (noise < min)
            {
                min = noise;
            }
            if (showValue)
            {
                int value = (int)(noise * 255);
                img->setPixelDP(
                    i, j, 0xFF000000 | (value << 16) | (value << 8) | value);
            }
            else
            {
                if (noise < border && noise > border2)
                {
                    img->setPixelDP(i, j, 0xFFFFFFFF);
                    counter++;
                }
                else
                {
                    img->setPixelDP(i, j, 0xFF000000);
                }
            }
        }
    }
    float percentage
        = ((float)counter / (img->getBreite() * img->getHeight())) * 100;
    std::cout << "Showing " << minP.x << " " << minP.y << " to " << maxP.x
              << " " << maxP.y << " at height " << position.z << " with border "
              << border2 << " to " << border << " true for " << percentage
              << "% of "
              << (img->getBreite() / zoom) * (img->getHeight() / zoom)
              << " blocks. Min: " << min << " Max: " << max << std::endl;
}

int main()
{
    A* c = new C();
    A* b = new B();
    A* a = new A();
    std::cout << typeid(*c).name() << std::endl;
    std::cout << typeid(*b).name() << std::endl;
    std::cout << typeid(*a).name() << std::endl;
    delete a;
    delete b;
    delete c;
    /*
    Framework::initFramework();
    FastNoiseLite* noise = new FastNoiseLite(1);
    noise->SetNoiseType(FastNoiseLite::NoiseType::NoiseType_Perlin);
    FastNoiseWrapper *wrapper1 = new FastNoiseWrapper(noise, 0);
    wrapper1->setMultiplier(0.05);
    Noise* baseHeight = new FlattenNoise(wrapper1, 0.5, 0.25);
    //wrapper = wrapper2;
    /* FastNoiseLite* noise = new FastNoiseLite(0);
    noise->SetNoiseType(FastNoiseLite::NoiseType::NoiseType_ValueCubic);
    noise->SetFrequency(3.f);
    wrapper = new FastNoiseWrapper(noise, 0);
     FastNoiseLite* n = new FastNoiseLite(2);
    n->SetNoiseType(FastNoiseLite::NoiseType::NoiseType_Cellular);
    n->SetFrequency(0.005f);
    n->SetRotationType3D(FastNoiseLite::RotationType3D::RotationType3D_None);
    n->SetCellularDistanceFunction(FastNoiseLite::CellularDistanceFunction::CellularDistanceFunction_Euclidean);
    n->SetCellularJitter(1.5f);
    n->SetCellularReturnType(
        FastNoiseLite::CellularReturnType::CellularReturnType_Distance);
    n->SetFractalType(
        FastNoiseLite::FractalType::FractalType_DomainWarpIndependent);
    n->SetDomainWarpType(
        FastNoiseLite::DomainWarpType::DomainWarpType_OpenSimplex2Reduced);
    n->SetDomainWarpAmp(100.f);
    n->SetFractalOctaves(3.f);
    n->SetFractalLacunarity(2.f);
    n->SetFractalGain(0.5f);
    FastNoiseWrapper* wrapper2 = new FastNoiseWrapper(n, 2);
    wrapper2->setMultiplier(0.4f);
    Noise* mountainRegion
        = new RescaleNoise(new NegatedNoise(new
   RescaleNoise(wrapper2, 3.5)), 1.5); noise = new FastNoiseLite(3);
    noise->SetNoiseType(FastNoiseLite::NoiseType::NoiseType_Perlin);
    noise->SetFrequency(0.25f);
    wrapper2 = new FastNoiseWrapper(noise, 0);
    noise = new FastNoiseLite(4);
    noise->SetNoiseType(FastNoiseLite::NoiseType::NoiseType_Perlin);
    noise->SetFrequency(0.02f);
    wrapper1 = new FastNoiseWrapper(noise, 0);
    Noise *mountains = new FactorizeNoise(wrapper1, wrapper2, 0.9);
    wrapper = new FactorizeNoise(
        baseHeight, new MultiplyNoise(mountains, mountainRegion), 0.5);
    //wrapper = new NoiseCombinerM(wrapper, wrapper2);
   // wrapper = new NoiseCombinerA(wrapper, wrapper2);
    //wrapper = new ShapedNoise(wrapper);
    //((ShapedNoise*)wrapper)->setNeighborOffset(4.f);
    //wrapper = new RandNoise(34255);

    img = new Bild();
    img->neuBild(1600, 1600, 0xFF000000);

    BildZ* view = new BildZ();
    view->setBildZ(img);
    view->setStyle(BildZ::Style::Sichtbar);
    view->setSize(800, 800);

    WNDCLASS wc = Framework::F_Normal(GetModuleHandle(NULL));
    wc.lpszClassName = "Fenster";
    window = new WFenster();
    window->erstellen(WS_OVERLAPPEDWINDOW, wc);
    window->setSize(800, 800);
    window->setPosition(100, 100);
    window->setAnzeigeModus(SW_SHOW);
    window->setVSchlie�Aktion([](void* p, void* o) {
        StopNachrichtenSchleife(window->getFensterHandle());
    });

    Bildschirm* screen = new Bildschirm2D(window);
    window->setBildschirm(dynamic_cast<Bildschirm*>(screen->getThis()));
    screen->addMember(view);
    screen->setTestRend(0);
    screen->update();
    screen->render();
    RenderTh* rth = new RenderTh();
    rth->setQuiet(1);
    rth->setBildschirm(screen);
    rth->beginn();

    updateView();

    new AsynchronCall([]() {
        char line[256];
        while (!exitF)
        {
            std::cin.getline(line, 256);
            if (strcmp(line, "exit") == 0)
            {
                StopNachrichtenSchleife(window->getFensterHandle());
                break;
            }
            Text txt(line);
            if (txt.positionVon("show ") == 0)
            {
                Text* x = txt.getTeilText(5);
                position.x = (int)*x;
                Text* y = x->getTeilText(x->positionVon(" ") + 1);
                position.y = (int)*y;
                Text* z = y->getTeilText(y->positionVon(" ") + 1);
                position.z = (int)*z;
                updateView();
                z->release();
                y->release();
                x->release();
            }
            if (txt.positionVon("border ") == 0)
            {
                Text* x = txt.getTeilText(7);
                border = (float)*x;
                updateView();
                x->release();
            }
            if (txt.positionVon("border2 ") == 0)
            {
                Text* x = txt.getTeilText(7);
                border2 = (float)*x;
                updateView();
                x->release();
            }
            if (txt.positionVon("zoom ") == 0)
            {
                Text* x = txt.getTeilText(5);
                zoom = (int)*x;
                updateView();
                x->release();
            }
            if (txt.positionVon("show value") == 0)
            {
                showValue = 1;
                updateView();
            }
            if (txt.positionVon("show border") == 0)
            {
                showValue = 0;
                updateView();
            }
        }
    });

    StartNachrichtenSchleife();

    exitF = 1;

    rth->beenden();
    window->setBildschirm(0);
    rth->release();
    Framework::releaseFramework();*/
    return 0;
}