#include ".\universe.h"
#include "./random.h"
#include "./LD.h"

#include <set>



Universe::Universe() :
  m_pFinalPlanet( NULL )
{
}



std::string Universe::RandomName( int Seed )
{

  static std::string    sillable1[] = { "Hol", "Jan", "Jak", "Sat", "Ur", "Lin", "Huh", "Xol", "Arto", "Ter" };
  static std::string    sillable2[] = { "tax", " Wod", "leb", "ubba", "an", "ra", "dro", "tre", "kot", "fun" };
  static std::string    sillable3[] = { "dum", "lu", "pux", "pox", "xor", "ton", "korn", "zup", "vega", "pon" };

  random    rng( Seed );

  int   numSil = 2 + rng.rand( 2 );

  std::string   name = sillable1[rng.rand( 10 )];
  name += sillable2[rng.rand( 10 )];
  if ( numSil == 3 )
  {
    name += sillable3[rng.rand( 10 )];
  }
  return name;

}



void Universe::CreateFromSeed( int Seed )
{

  m_pFinalPlanet = NULL;

  random                rng( Seed );

  std::set<tPoint>      setStars;


  int     numberOfStars = 2048 + rng.rand( 512 );

  int     longestDistance = 0;

  for ( int i = 0; i < numberOfStars; ++i )
  {
    Universe::Star    star;

    found:;
    star.Location.x = 10 + rng.rand( 620 );
    star.Location.y = 10 + rng.rand( 380 );

    for ( int x = -2; x <= 2; ++x )
    {
      for ( int y = -2; y <= 2; ++y )
      {
        if ( setStars.find( tPoint( star.Location.x + x, star.Location.y + y ) ) != setStars.end() )
        {
          goto found;
        }
      }
    }
    setStars.insert( star.Location );
    star.Distance   = 30 + rng.rand( 14000 );
    star.Seed       = rng.seed();
    star.Name       = RandomName( star.Seed );
    star.Type       = rng.rand( 4 );

    if ( star.Distance > longestDistance )
    {
      longestDistance = star.Distance;
    }

    size_t  planetCount = 1 + rng.rand( 10 );
    for ( size_t j = 0; j < planetCount; ++j )
    {
      Universe::Planet    planet;

      int   rand = rng.rand( 100 );
      if ( rand >= 50 )
      {
        planet.PlanetType |= Universe::PT_ROCK;
      }
      else if ( rand > 40 )
      {
        planet.PlanetType |= Universe::PT_DIRT;
      }
      else if ( rand > 30 )
      {
        planet.PlanetType |= Universe::PT_ICE;
      }
      else if ( rand > 20 )
      {
        planet.PlanetType |= Universe::PT_LAVA;
      }
      else if ( rand > 10 )
      {
        planet.PlanetType |= Universe::PT_FIRE;
      }
      else
      {
        planet.PlanetType |= Universe::PT_WATER;
      }
      star.Planets.push_back( planet );
    }

    tPoint      gridPos;

    gridPos = star.Location / 40;

    if ( i == 0 )
    {
      star.Distance = 4;
    }

    for ( size_t j = 0; j < planetCount; ++j )
    {
      if ( HasPlanetResource( &star, (int)j, LD::RT_ALIEN_TECHNOLOGY ) )
      {
        star.HasAlienTech = true;
        break;
      }
    }

    m_Stars[gridPos].push_back( star );

    if ( i == numberOfStars -1 )
    {
      int   finalPlanetIndex = rng.rand( (int)m_Stars[gridPos].back().Planets.size() );

      m_pFinalPlanet = &m_Stars[gridPos].back().Planets[finalPlanetIndex];
      m_Stars[gridPos].back().HasAlienTech = true;
    }
  }

}



void Universe::DisplayStars( IDirect3DDevice8* pDevice, int AllowedDistance )
{

  bool    hasScanner = theApp.m_PlayerData.HasBought( LD::SI_STAR_SCANNER );

  theApp.m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
  theApp.m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );

  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

  std::map<tPoint,std::list<Star> >::iterator   itStars( m_Stars.begin() );
  while ( itStars != m_Stars.end() )
  {
    std::list<Star>&    stars( itStars->second );

    std::list<Star>::iterator   itStar( stars.begin() );
    while ( itStar != stars.end() )
    {
      Star&   star( *itStar );

      tTextureSection   texSec( theApp.m_TexSec[LD::TEX_STAR_1 + star.Type] );

      unsigned int      starColor = 0xffffffff;

      if ( star.Distance > AllowedDistance )
      {
        starColor = 0xff404040;
      }
      else if ( ( hasScanner )
      &&        ( star.HasAlienTech ) )
      {
        starColor = 0xff60ff60;
      }

      theApp.RenderTextureSection2d( star.Location.x - texSec.m_iWidth / 2, star.Location.y - texSec.m_iHeight / 2, texSec, starColor );

      ++itStar;
    }

    ++itStars;
  }

}



void Universe::DisplayPlanet( IDirect3DDevice8* pDevice, Planet& Planet, int OffsetX, int OffsetY )
{

  theApp.m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
  theApp.m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );

  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  theApp.m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

  int   X1 = OffsetX / 40 - 1;
  int   X2 = ( OffsetX + 640 ) / 40 + 2;
  int   Y1 = OffsetY / 40 - 1;
  int   Y2 = ( OffsetY + 400 ) / 40 + 2;

  int   DX = OffsetX % 40;
  int   DY = OffsetY % 40;

  for ( int y = Y1; y <= Y2; ++y )
  {
    for ( int x = X1; x <= X2; ++x )
    {
      theApp.RenderTextureSection2d( ( x - X1 ) * 40 - DX, ( y - Y1 ) * 40 - DY, theApp.m_TexSec[theApp.m_Tiles[Tile( x, y ).Type] ] );

    }
  }

}



Universe::tTile& Universe::Tile( int X, int Y )
{

  static tTile    Dummy;

  while ( X < 0 )
  {
    X += 200;
  }
  while ( X >= 200 )
  {
    X -= 200;
  }
  while ( Y < 0 )
  {
    Y += 200;
  }
  while ( Y >= 200 )
  {
    Y -= 200;
  }
  return m_Tiles[X][Y];

}



bool Universe::HasPlanetResource( Star* pStar, int Planet, LD::ResourceType Resource )
{

  if ( m_pFinalPlanet == &pStar->Planets[Planet] )
  {
    if ( Resource == LD::RT_ALIEN_TECHNOLOGY )
    {
      return true;
    }
  }

  random    rng( pStar->Seed + Planet );

  int   numResources = rng.rand( 8 );

  std::set<tPoint>    usedLocations;

  for ( int i = 0; i < numResources; ++i )
  {
    LD::Resource    resource;

    // make sure resources do not occupy same location
    do
    {
      resource.Location.x = rng.rand( 200 * 40 );
      resource.Location.y = rng.rand( 200 * 40 );
    }
    while ( usedLocations.find( resource.Location ) != usedLocations.end() );
    usedLocations.insert( resource.Location );

    resource.Quantity   = 1 + rng.rand( 1024 );

    resource.Type       = (LD::ResourceType)rng.rand( LD::RT_LAST_ENTRY );
    if ( resource.Type == LD::RT_ALIEN_TECHNOLOGY )
    {
      if ( rng.rand( 100 ) < 98 )
      {
        resource.Type = LD::RT_ZINC;
      }
    }

    if ( resource.Type == Resource )
    {
      if ( !theApp.m_PlayerData.HasCollected( pStar, Planet, resource ) )
      {
        return true;
      }
    }
  }
  return false;

}



void Universe::FetchPlanet( Star* pStar, int Planet, std::list<LD::Resource>& Resources )
{

  // this on top!
  random    rng( pStar->Seed + Planet );

  int   numResources = rng.rand( 8 );

  std::set<tPoint>    usedLocations;

  for ( int i = 0; i < numResources; ++i )
  {
    LD::Resource    resource;

    // make sure resources do not occupy same location
    do
    {
      resource.Location.x = rng.rand( 200 * 40 );
      resource.Location.y = rng.rand( 200 * 40 );
    }
    while ( usedLocations.find( resource.Location ) != usedLocations.end() );
    usedLocations.insert( resource.Location );

    resource.Quantity   = 1 + rng.rand( 1024 );

    resource.Type       = (LD::ResourceType)rng.rand( LD::RT_LAST_ENTRY );
    if ( resource.Type == LD::RT_ALIEN_TECHNOLOGY )
    {
      if ( rng.rand( 100 ) < 98 )
      {
        resource.Type = LD::RT_ZINC;
      }
    }

    if ( !theApp.m_PlayerData.HasCollected( pStar, Planet, resource ) )
    {
      Resources.push_back( resource );
    }
  }

  if ( m_pFinalPlanet == &pStar->Planets[Planet] )
  {
    // the goal!
    LD::Resource    resBase;

    resBase.Location.set( rng.rand( 200 * 40 ), rng.rand( 200 * 40 ) );
    resBase.Quantity  = 1;
    resBase.Type      = LD::RT_ALIEN_TECHNOLOGY;
    Resources.push_back( resBase );
  }


  LD::TileType    baseTile = LD::TT_ROCK;
  LD::TileType    secondaryTile = LD::TT_ROCK_2;

  switch ( pStar->Planets[Planet].PlanetType )
  {
    case Universe::PT_LAVA:
      baseTile = LD::TT_LAVA;
      secondaryTile = LD::TT_LAVA_2;
      break;
    case Universe::PT_ICE:
      baseTile = LD::TT_ICE;
      secondaryTile = LD::TT_ICE_2;
      break;
    case Universe::PT_DIRT:
      baseTile = LD::TT_DIRT;
      secondaryTile = LD::TT_DIRT_2;
      break;
    case Universe::PT_WATER:
      baseTile = LD::TT_WATER;
      secondaryTile = LD::TT_WATER_2;
      break;
    case Universe::PT_FIRE:
      baseTile = LD::TT_FIRE;
      secondaryTile = LD::TT_FIRE_2;
      break;
    case Universe::PT_SAND:
      baseTile = LD::TT_SAND;
      secondaryTile = LD::TT_SAND_2;
      break;
  }

  for ( int i = 0; i < 200; ++i )
  {
    for ( int j = 0; j < 200; ++j )
    {
      tTile&    tile( Tile( i, j ) );

      tile.Type     = baseTile;

      if ( rng.rand( 2 ) )
      {
        tile.Type = secondaryTile;
      }
    }
  }

  // TODO - random tiles damit's nicht langweilig ist


}



std::string Universe::ResourceName( LD::ResourceType Resource )
{

  switch ( Resource )
  {
    case LD::RT_ALIEN_TECHNOLOGY:
      return "Alien Tech";
    case LD::RT_CRYSTITE:
      return "Crystite";
    case LD::RT_GOLD:
      return "Gold";
    case LD::RT_SPICE:
      return "Spice";
    case LD::RT_UNOBTAINIUM:
      return "Unobtanium";
    case LD::RT_ZINC:
      return "Zinc";
  }

  return "None";

}



