#include 
#include 
#include 
#include 
using std::cout;
using std::endl;

#include "game_jam_game.hpp"
#include "input.hpp"
#include "render.hpp"
#include "texture.hpp"
#include "audio.hpp"

GameJamGame::GameJamGame()
: state (TitleScreen)
, musicPlaying ()
{
    /**
     * Init audio and start playing background music.
     */

    snd_stream_init();
    sndoggvorbis_init();

    /**
     * Populate cache.
     */

    Reload();
}

StaticMesh GameJamGame::LoadStaticMesh(std::string const & sMeshName,
                                       std::string const & sMeshFile,
                                       std::string const & sTextureFile,
                                       uint32_t textureWidth,
                                       uint32_t textureHeight)
{
    cout << "Loading static mesh: " << sMeshName << " (" << sMeshFile << ", " << sTextureFile << ")..." << endl;

    auto it = staticMeshes.find(sMeshName);

    if(it != staticMeshes.end())
    {
        cout << sMeshName << " was cached" << endl;
        return it->second;
    }

    cout << sMeshName << " was not cached - loading." << endl;

    StaticMesh sm = StaticMesh::LoadFromFile(sMeshFile);
    sm.SetTexture(LoadTexture(sTextureFile, textureWidth, textureHeight), textureWidth, textureHeight);
    staticMeshes[sMeshName] = sm;
    return sm;
}

SkeletalMesh GameJamGame::LoadSkeletalMesh(std::string const & sMeshName,
                                           std::string const & sMeshFile,
                                           std::string const & sSkeletonFile,
                                           std::string const & sTextureFile,
                                           uint32_t textureWidth,
                                           uint32_t textureHeight)
{
    cout << "Loading skeletal mesh: " << sMeshName << " (" << sMeshFile << ", " << sSkeletonFile << ", " << sTextureFile << ")..." << endl;

    auto it = skeletalMeshes.find(sMeshName);

    if(it != skeletalMeshes.end())
    {
        cout << sMeshName << " was cached" << endl;
        return it->second;
    }

    cout << sMeshName << " was not cached - loading." << endl;

    SkeletalMesh sm = SkeletalMesh::LoadFromFile(sMeshFile, sSkeletonFile, sTextureFile, 512, 512);
    skeletalMeshes[sMeshName] = sm;
    return sm;
}

void * GameJamGame::LoadTexture(std::string const & sTextureFile,
                                uint32_t width,
                                uint32_t height)
{
    cout << "Loading texture: " << sTextureFile << "..." << endl;

    auto it = textures.find(sTextureFile);

    if(it != textures.end())
    {
        cout << sTextureFile << " was cached" << endl;
        return it->second;
    }

    cout << sTextureFile << " was not cached - loading." << endl;

    pvr_ptr_t tex;
    load_texture(&tex, 512 * 512 * 2, sTextureFile.c_str());
    textures[sTextureFile] = tex;

    cout << sTextureFile << " loaded" << endl;
    return static_cast(tex);
}

tSharedAnimation GameJamGame::LoadAnimation(std::string const & sAnimationName, std::string const & sAnimationFile)
{
    cout << "Loading animation: " << sAnimationName << endl;

    auto it = animations.find(sAnimationName);

    if(it != animations.end())
    {
        cout << sAnimationName << " was cached" << endl;
        return it->second;
    }

    cout << sAnimationFile << " was not cached - loading." << endl;

    tSharedAnimation animation = std::make_shared();
    animation->LoadFromFile(sAnimationFile);
    animations[sAnimationName] = animation;
    
    cout << sAnimationFile << " loaded" << endl;
    return animation;
}

void GameJamGame::Reload()
{
    titleScreen = LoadTexture("rd/title.png", 512, 512);

    camera = Camera(Vector(0, 6, -5));

    //skeleton = LoadSkeletalMesh("skeleton", "rd/test.ross_mesh", "rd/test.ross_skel", "rd/test.ross_anim", "rd/skeleton.png", 512, 512);

    /**
     * Populate dungeon with rooms:
     */

    dungeon.Reset();

    for(size_t i = 1; i <= 4; ++i)
    {
        std::stringstream ss;
        ss << "room" << i;

        Room room;

        Room::RoomData data;
        data.id = i;
        
        switch(data.id)
        {
            case 1:
                data.halfWidth = 3.5f;
                data.halfHeight = 2.5f;

                data.playerStarts[0] = Vector(0, 0, 0);
                data.playerStarts[1] = Vector(0, 0, 2.3f);
                break;

            case 2:
                data.halfWidth = 1.75f;
                data.halfHeight = 5.35f;

                data.playerStarts[0] = Vector(0, 0, -5.9f);
                data.playerStarts[1] = Vector(0, 0, 5.75f);
                break;

            case 3:
                data.halfWidth = 5.0f;
                data.halfHeight = 2.75f;

                data.playerStarts[0] = Vector(0, 0, -2.3f);
                data.playerStarts[1] = Vector(0, 0, 2.3f);
                break;

            case 4:
                data.halfWidth = 10.5f;
                data.halfHeight = 11.5f;

                data.playerStarts[0] = Vector(0, 0, -10.5f);
                break;

            default:
                data.halfWidth = 5.0f;
                data.halfHeight = 2.75f;

                data.playerStarts[0] = Vector(0, 0, 0);
                break;
        }

        /**
        data.underdemonMesh = LoadStaticMesh("underdemon", "rd/goblin.ross_mesh", "rd/goblin.png", 512, 512);
        data.skeletonMesh = LoadStaticMesh("skeleton", "rd/skeleton.ross_mesh", "rd/skeleton.png", 512, 512);
        data.knightMesh = LoadStaticMesh("knight", "rd/knight.ross_mesh", "rd/knight.png", 512, 512);
        **/

        data.underdemonMesh = LoadSkeletalMesh("goblin", "rd/goblin.ross_mesh", "rd/ross.ross_skel", "rd/goblin.png", 512, 512);
        data.skeletonMesh = LoadSkeletalMesh("skeleton", "rd/skeleton.ross_mesh", "rd/skeleton.ross_skel", "rd/skeleton.png", 512, 512);
        data.knightMesh = LoadSkeletalMesh("knight", "rd/knight.ross_mesh", "rd/ross.ross_skel", "rd/knight.png", 512, 512);

        data.rossMesh = LoadSkeletalMesh("ross", "rd/ross.ross_mesh", "rd/ross.ross_skel", "rd/ross.png", 512, 512);
        data.allyMesh = LoadSkeletalMesh("ally", "rd/ally.ross_mesh", "rd/ross.ross_skel", "rd/ally.png", 512, 512);
        data.doorMesh = LoadStaticMesh("door", "rd/door.ross_mesh", "rd/dungeon.png", 512, 512);
        data.boxMesh = LoadStaticMesh("box", "rd/box.ross_mesh", "rd/box.png", 256, 256);
        StaticMesh roomMesh = LoadStaticMesh(ss.str(), "rd/" + ss.str() + ".ross_mesh", "rd/dungeon.png", 512, 512);

        data.playerIdle = LoadAnimation("player_idle", "rd/ross_idle.ross_anim");
        data.playerWalk = LoadAnimation("player_walk", "rd/ross_walk.ross_anim");
        data.playerRun = LoadAnimation("player_run", "rd/ross_run.ross_anim");
        data.playerAttack = LoadAnimation("player_attack", "rd/ross_attack.ross_anim");

        data.enemyWalk = data.playerWalk;

        room.SetRoomData(data);
        room.SetStaticMesh(roomMesh);
        dungeon.AddRoom(i, room);
    }

    dungeon.SetCurrentRoom(1, 0, false);

    cout << "Finished reloading - memory usage:" << endl;
    malloc_stats();
}

GameJamGame::~GameJamGame()
{
    sndoggvorbis_stop();
    sndoggvorbis_shutdown();
    snd_stream_shutdown();
}

void GameJamGame::Process(float deltaTime, Input * pInput)
{
    /**
     * Cap delta time at 1s.
     */

    if(deltaTime > 1)
    {
        deltaTime = 1;
    }

    cont_state_t * st = pInput->pFirstControllerState;

    if(st != NULL)
    {
        static bool startPressed;

        if(st->buttons & CONT_START)
        {
            if(startPressed == false)
            {
                startPressed = true;
            }
        }
        else if(startPressed == true)
        {
            if(state == Gameplay)
            {
                state = TitleScreen;

                if(musicPlaying == false)
                {
                    sndoggvorbis_start("/rd/bgmusic.ogg", 1 /* loop */);   
                    musicPlaying = true;
                }
            }
            else if(state == TitleScreen)
            {
                Reload();
                state = Gameplay;
            }

            startPressed = false;
        }

        if(state == TitleScreen || state == Gameplay)
        {
            static bool yPressed;

            if(st->buttons & CONT_Y)
            {
                if(yPressed == false)
                {
                    yPressed = true;
                }
            }
            else if(yPressed == true)
            {
                if(musicPlaying == false)
                {
                    sndoggvorbis_start("/rd/bgmusic.ogg", 1 /* loop */);
                    musicPlaying = true;
                }
                else
                {
                    sndoggvorbis_stop();
                    musicPlaying = false;
                }

                yPressed = false;
            }
        }
    }

    if(state == Gameplay)
    {
        camera.Process(deltaTime, pInput);
        dungeon.Process(deltaTime, pInput);

        camera.SetTarget(dungeon.GetCameraFocalPoint());
        camera.SetElevation(dungeon.GetCameraElevation());
   }
}

void GameJamGame::Draw()
{
    if(state == TitleScreen)
    {
        static float const w = 640;
        static float const h = 480;

        static tQuads quads {

            Quad(Vertex(0, 0, 1, 0, 0),
                 Vertex(w, 0, 1, 1, 0),
                 Vertex(0, h, 1, 0, 1),
                 Vertex(w, h, 1, 1, 1))
        };

        render::begin_frame();
        pvr_list_begin(PVR_LIST_OP_POLY);
        render::draw_textured_quads(quads, titleScreen, 512, 512);
        pvr_list_finish();
        render::end_frame();
    }
    else if(state == Gameplay)
    {
        /**
         * Apply the current view projection matrix.
         */

        Matrix const & vp = camera.GetViewProjection();
        dungeon.SetViewProjection(vp);

        /**
         * Draw entities.
         */

        render::begin_frame();
        pvr_list_begin(PVR_LIST_OP_POLY);

        dungeon.Draw();

        pvr_list_finish();
        render::end_frame();
    }
}
    

Dundee

code.compare();

// What?

Welcome to code.compare(); - Dundee's fortnightly coding meet-up! Share and compare code with developers of all backgrounds and skill levels, have a chat, and get some help with your coding projects.

// Why?

Coders are continually learning, experimenting and finding better ways to build cool things.

As it happens, this gets much easier and more fun when we pool our resources and lend one another a hand.

// When?

Technically, we haven't started yet!

The plan is to have a small inaugural meeting this Wednesday (19th of June, 2017) and take it from there.

After this, code.compare(); will run:

  -> Every fortnight
  -> on the first and third Wednesday each month
  -> from 6:30pm to 8:30pm

// Where?

Dundee MakerSpace
Unit 5
The Vision Building
Dundee
DD2 4QB
UK

// How?

void comeAlong()
{
    coder.contact("rosskilgariff@gmail.com");

    coder.bring(laptop, project);

    coder.chatWith(otherCoders);

    coder.compare(code).with(otherCoders.code);

    coder.eat(SNACKS);

    if (coder.isFeeling(SUPPORTIVE | TALENTED))
    {
        coder.help(otherCoders);
    }

    coder.have(FUN);
}

// Pay What You Want!

code.compare(); is run by volunteers, and donations are welcome!

If you'd like to donate, there's always a cash box at the door.

Aside from buying snacks, all donations go to our host organisation - Dundee MakerSpace.

Run by ross.codes and Dundee MakerSpace