local love = assert( love )
local ffi = require( "ffi" )
local sideLength  = 128 --VERTICES in a side
local vertexCount = sideLength * sideLength
local vertices = love.data.newByteData( 4 * 2 * vertexCount )

local heightMap = love.image.newImageData( "tex/terrain.jpg" )
local heightTex = love.graphics.newImage( heightMap, {linear = true} )
heightTex:setWrap( "repeat", "repeat" )

local mesh = love.graphics.newMesh(
  {{"VertexPosition", "float", 2 }},
  vertexCount,
  "triangles",
  "static"
)

--set vertices
do
  local p = vertices:getFFIPointer()
  local ptr = ffi.cast( "float*", p )
  local i = 0
  for x = 0, sideLength-1 do for y = 0, sideLength-1 do
    ptr[i],ptr[i+1] = x - sideLength / 2 ,y - sideLength / 2
    i=i+2
  end end
  mesh:setVertices( vertices )
  do local _ = p end
end

--set vertex map
do
  local ptr = {}
  local n = 1
  local s = sideLength
  for x = 1, sideLength - 1 do for y = 0, sideLength - 2 do
    local i = y*s+x
    --print( x, y, i, i+1, i+s, i+s+1 )
    ptr[n],ptr[n+1],ptr[n+2] = i,i+1,i+s
    n=n+3
    ptr[n],ptr[n+1],ptr[n+2] = i+1,s+i+1,i+s
    n=n+3
  end end
  mesh:setVertexMap( ptr )
end

local shader = love.graphics.newShader[[
  #define fog vec4( 0.6, 0.6, 0.7, 1.0 )
  #define fogHigh vec4( 1.0, 1.0, 0.9, 1.0 )
  #define fogFalloff 0.03
  varying vec2 uv;
  varying vec3 world;
  varying vec3 normal;
  varying float depth;
#ifdef VERTEX
  uniform sampler2D height;
  uniform mat4 view;
  uniform mat4 proj;
  
  float sigmoid( float x ){
    return 1.0 / ( 1.0 + exp( -x ) );
  }
  
  vec4 position( mat4 _, vec4 pos ){
    pos = pos.xzyw;
    float y = Texel( height, pos.xz / 256.0 ).r - 0.5;
    uv = 0.25 * (pos.xz + vec2(y, sin( y ) ));
    float r = dot( pos.xz, pos.xz );
    pos.y = sigmoid( r / 50.0 - 20.0 ) * ( 1.0 + y ) * 0.05 * r / (sqrt( r ) + 1) - 0.01;
    
    world = pos.xyz;
    vec4 eye = view*pos;
    depth = clamp( -eye.z * fogFalloff, 0.0, 1.0 );
    
    normal = vec3( 0.0, 1.0, 0.0 ) + 1.5 * pos.y ;
    
    return proj*eye;
  }
#endif
#ifdef PIXEL
  uniform vec3 light;
  vec4 effect( vec4 color, Image tex, vec2 texuv, vec2 scruv) {
  
    vec3 albedo = 0.2 * Texel( tex, uv ).rgb;
  
    //diffuse light
    vec3 norm  = normalize( normal );
    vec3 toLight = normalize( light - world.xyz );
    float diff = max( dot( norm, light ), 0.1 );
    vec4 d = vec4( diff * albedo, 1.0 );
    
    return mix( color * d , fog, depth );
  }
#endif
]]

--set texture
do
  local tex = love.graphics.newImage( "tex/rock.png", {mipmaps = true} )
  tex:setWrap( "repeat", "repeat" )
  mesh:setTexture( tex )
end

shader:send( "height", heightTex )

local obj = {}

local function worldSpaceToTexCoord( x, z )
  return x, z
end

function obj.getHeight( x, z )
  return heightMap:getPixel( worldSpaceToTexCoord(x,z) )[1]
end

function obj.draw( view, proj, light )
  shader:send( "view", "column", view )
  shader:send( "proj", "column", proj )
  shader:send( "light", light )
  love.graphics.setShader( shader )
  love.graphics.draw( mesh )
end

return obj