##########################################################################
# This file is part of Vacuum Magic
# Copyright (C) 2008-2009 by UPi <upi at sourceforge.net>
##########################################################################

use strict;

##########################################################################
# GRAPHICS-RELATED SUBS
##########################################################################

package main;

sub LoadSurfaces {
  new Texture('background1', "$DataDir/background1.png", -0.5);
  new Texture('background2', "$DataDir/background2.png", -0.5);
  new Texture('background3', "$DataDir/background3.png", -0.5);
  new Texture('bg3_sky', "$DataDir/bg3_sky.png", -0.5);
  new Texture('bg3_mountain', "$DataDir/bg3_mountain.png", -0.5);
  new Texture('bg4_nebula', "$DataDir/bg4_nebula.png", -0.5);
  new Texture('stars1', "$DataDir/stars1.png");
  new Texture('stars2', "$DataDir/stars2.png");
  new Texture('stars3', "$DataDir/stars3.png");
  new Texture('stars4', "$DataDir/stars4.png");
  new Texture('bg5_ground', "$DataDir/bg5_ground.png", -0.5);
  new Texture('ball', "$DataDir/ball.png");
  new Texture('cloud', "$DataDir/cloud.png", -0.4);
  new Texture('enemy', "$DataDir/enemy.png");
  new Texture('rock', "$DataDir/rock.png");
  new Texture('reddragon', "$DataDir/reddragon.png");
  new Texture('bluedragon', "$DataDir/bluedragon.png");
  new Texture('logo', "$DataDir/logo.png");
  new Texture('particle', "$DataDir/particle.png");
  new Texture('player', "$DataDir/player.png");
  new Texture('swirl', "$DataDir/swirl.png");
  new Texture('slurp', "$DataDir/slurp2.png", 0.4);
  new Texture('bonus', "$DataDir/bonus.png", 0.4);
  new Texture('bossvitality', "$DataDir/bossvitality.png");
  new Texture('circles', "$DataDir/circles.png");
  new Texture('lava', "$DataDir/lava.png");
  new Texture('artic1', "$DataDir/artic1.png");
  new Texture('artic2', "$DataDir/artic2.png");
  new Texture('artic3', "$DataDir/artic3.png");
  new Texture('artic4', "$DataDir/artic4.png");
  new Texture('artic5', "$DataDir/artic5.png");
  new Texture('starfield', "$DataDir/starfield.png");
  new Texture('cavern1', "$DataDir/cavern1.png");
  new Texture('cavern2', "$DataDir/cavern2.png");
  new Texture('bomber', "$DataDir/bomber.png");
  new Texture('halloween1', "$DataDir/halloween1.png");
  new Texture('halloween2', "$DataDir/halloween2.png");
  new Texture('halloween3', "$DataDir/halloween3.png");
  new Texture('halloween4', "$DataDir/halloween4.png");
  new Texture('halloween_moon1', "$DataDir/halloween_moon1.png");
  new Texture('halloween_moon2', "$DataDir/halloween_moon2.png");
  new Texture('ancient_forest', "$DataDir/ancient_forest.png");
  new Texture('ancient_forest2', "$DataDir/ancient_forest2.png");
  new Texture('ancient_forest3', "$DataDir/ancient_forest3.png");
  new Texture('desert', "$DataDir/desert.png");
  new Texture('papakoules', "$DataDir/papakoules.png");

  $Textures{'bg3_sky'}->SetRepeatX();
  $Textures{'bg3_mountain'}->SetRepeatX();
  $Textures{'bg4_nebula'}->SetRepeatX();
  $Textures{'stars1'}->SetRepeatX();
  $Textures{'stars1'}->SetRepeatY();
  $Textures{'stars2'}->SetRepeatX();
  $Textures{'stars2'}->SetRepeatY();
  $Textures{'stars3'}->SetRepeatX();
  $Textures{'stars3'}->SetRepeatY();
  $Textures{'stars4'}->SetRepeatX();
  $Textures{'stars4'}->SetRepeatY();
  $Textures{'bg5_ground'}->SetRepeatX();
  $Textures{'lava'}->SetRepeatX();
  $Textures{'artic1'}->SetRepeatX();
  $Textures{'artic2'}->SetRepeatX();
  $Textures{'artic3'}->SetRepeatX();
  $Textures{'artic4'}->SetRepeatX();
  $Textures{'starfield'}->SetRepeatX();
  $Textures{'starfield'}->SetRepeatY();
  $Textures{'circles'}->DisableAntialiasing();
  $Textures{'cavern1'}->SetRepeatX();
  $Textures{'cavern2'}->SetRepeatX();
  $Textures{'cavern2'}->SetRepeatY();
  $Textures{'halloween1'}->SetRepeatX();
  $Textures{'halloween4'}->SetRepeatX();
  $Textures{'ancient_forest2'}->SetRepeatX();
  $Textures{'ancient_forest3'}->SetRepeatX();
  $Textures{'desert'}->SetRepeatX();
  
  $ScoreFont = new GlFont('file' => "$DataDir/courier2.png", 'spacing_x' => -2, 'transparent' => 0, );
  $BrandyFont = new GlFont('file' => "$DataDir/brandybun.png", 'transparent' => 0, );
  $GlossyFont = new GlFont('file' => "$DataDir/glossy3.png", 'spacing_x' => -5, 'transparent' => 0, 'keepSurface' => 1 );
  
  foreach (qw(ball bonus bossvitality enemy particle player slurp swirl)) {
    $Textures{$_}->SetPermanent();
  }
}

sub TextWidth {
  if (defined(&SDL::App::SDL_TEXTWIDTH)) {
    SDL::App::SDL_TEXTWIDTH(@_);   # perl-sdl-1.x
  } else {
    SDL::SFont::SDL_TEXTWIDTH(@_); # perl-sdl-2.x
  }
}

sub FindVideoMode {
  # TODO check video dimensions SDL::VideoModeOK
  return ($PhysicalScreenWidth, $PhysicalScreenHeight);
}

sub ChangeResolution {
  my ($newPhypsicalScreenWidth) = @_;
  
  return  if $PhysicalScreenWidth == $newPhypsicalScreenWidth;
  $PhysicalScreenWidth = $newPhypsicalScreenWidth;
  $PhysicalScreenHeight = $PhysicalScreenWidth * 0.75;
  $App->resize($PhysicalScreenWidth, $PhysicalScreenHeight);
  ::glViewport(0,0,$PhysicalScreenWidth, $PhysicalScreenHeight);
}

sub SaveScreenshot {
  my $i = 0;
  my $filename;
  do { $filename = sprintf("screenshot%03d.bmp", $i); ++$i } while (-f $filename);
  my $data = glReadPixels(0, 0,$PhysicalScreenWidth,$PhysicalScreenHeight, GL_BGR, GL_UNSIGNED_BYTE);
  &SaveBMP($filename, $PhysicalScreenWidth, $PhysicalScreenHeight, 24, $data);
}

sub SaveBMP {
  my ($file, $w, $h, $bpp, $data) = @_;

  my $datasize = int($w * $h * $bpp) / 8;
  my $headersize = 14;
  my $infoheadersize = 40;
  my $header = pack ("SLSSL", 0x4d42, $headersize, 0, 0, $headersize + $infoheadersize );
  my $infoheader = pack ("LLLSSLLLLLL", 
    $infoheadersize, $w, $h, 1, $bpp, 0, 	# 0 = uncompressed
    $datasize, 0, 0, 0, 0);

  my $FILE;
  open $FILE, ">$file" or die("Cannot write to $file: $!");
  binmode $FILE;
  print $FILE $header, $infoheader, $data;
  close $FILE;
}


##########################################################################
# OPENGL
##########################################################################

sub InitGL {
  my ($w, $h)= ($App->width, $App->height);
  print STDERR "Viewport size is $w x $h\n";

  glViewport(0, 0, $w, $h);
  glClearColor(0.0, 0.0, 0.0, 0.0);                            # This Will Clear The Background Color To Black
  glClearDepth(1.0);                                           # Enables Clearing Of The Depth Buffer
  glDepthFunc(GL_LESS);                                        # The Type Of Depth Test To Do
  glEnable(GL_DEPTH_TEST);                                     # Enables Depth Testing
  glShadeModel(GL_SMOOTH);                                     # Enables Smooth Color Shading
  
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();                                            # Reset The Projection Matrix
  
  glMatrixMode(GL_MODELVIEW);
  
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#  glEnable(GL_ALPHA_TEST);
#  glAlphaFunc(GL_GREATER, 0.1);
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);
  &CheckGLError();

  glEnable(GL_TEXTURE_2D);			# Enable Texture Mapping
}

sub CheckGLError {
  if (my $error = glGetError()) {
    Carp::confess gluErrorString($error);
  }
}


##########################################################################
# PALETTE MANIPULATION
##########################################################################


sub AlterPalette($$$) {
  my ($surface, $filterSub, $alterSub) = @_;
  my ($r, $g, $b);
  my ($palette, $numColors, $n, $color);

  $palette = $surface->palette();
  $numColors = SDL::PaletteNColors($palette);
  for ($n = 1; $n < $numColors; ++$n) {
    $color = SDL::PaletteColors($palette, $n);
    ($r, $g, $b) = ( SDL::ColorR($color), SDL::ColorG($color), SDL::ColorB($color) );

    next unless $filterSub->($n, $r, $g, $b);
    ($r, $g, $b) = $alterSub->($n, $r, $g, $b);
    $r = $g = $b = 4 if ($r == 0 and $g == 0 and $b == 0);

    SDL::PaletteColors($palette, $n, $r, $g, $b);
  }
  $surface->display_format();
}

sub RgbToHsi {
  my ($r, $g, $b) = @_;
  my ($min, $max, $delta, $h, $s, $i);

  if ($r > $g) {
    $max = $r > $b ? $r : $b;
    $min = $g < $b ? $g : $b;
  } else {
    $max = $g > $b ? $g : $b;
    $min = $r < $b ? $r : $b;
  }
  $i = ($min + $max) / 2;
  if ($min == $max) {
    return (0, 0, $i);
  }

  $delta = ($max - $min);
  if ($i < 128) {
    $s = 255 * $delta / ($min + $max);
  } else {
    $s = 255 * $delta / (511 - $min - $max);
  }

  if ($r == $max) {
    $h = ($g - $b) / $delta;
  } elsif ($g == $max) {
    $h = 2 + ($b - $r) / $delta;
  } else {
    $h = 4 + ($r - $g) / $delta;
  }
  $h = $h * 42.5;
  $h += 255 if $h < 0;
  $h -= 255 if $h > 255;

  return ($h, $s, $i);
}

sub HsiToRgb {
  my ($h, $s, $i) = @_;
  my ($m1, $m2);

  if ($s < 1) {
    $i = int($i + 0.5);
    return ($i, $i, $i);
  }

  if ($i < 128) {
    $m2 = ($i * (255 + $s)) / 65025.0;
  } else {
    $m2 = ($i + $s - ($i * $s) / 255.0) / 255.0;
  }
  $m1 = ($i / 127.5) - $m2;

  return (
    &GetHsiValue( $m1, $m2, $h + 85),
    &GetHsiValue( $m1, $m2, $h),
    &GetHsiValue( $m1, $m2, $h - 85)
  );
}

sub GetHsiValue {
  my ($n1, $n2, $hue) = @_;
  my ($value);

  $hue -= 255 if ($hue > 255);
  $hue += 255 if ($hue < 0);
  if ($hue < 42.5) {
    $value = $n1 + ($n2 - $n1) * ($hue / 42.5);
  } elsif ($hue < 127.5) {
    $value = $n2;
  } elsif ($hue < 170) {
    $value = $n1 + ($n2 - $n1) * ((170 - $hue) / 42.5);
  } else {
    $value = $n1;
  }
  return int($value * 255 + 0.5);
}

1;
