%% -*- erlang -*-
%% Config file for cl-application

%% This would have been nicer if it was an escript..
%% Start at the bottom and read upwards
{value, {port_env, PortEnv0}, Config0} = lists:keytake(port_env, 1, CONFIG).
GetEnv = fun(Var) -> 
		 case os:getenv(Var) of
		     [] -> false;
		     Other -> Other
		 end
	 end.

AddConf = fun(_Var, [], Config) -> Config;
	     (Var, Value, Config) -> 
		  lists:keystore(Var, 1, Config, {Var, Value})
	  end.

Error = fun(Format, Args) ->
		io:format("~n***ERROR:~n" ++ Format ++"~n~n",Args),
		throw(error)
	end.

ConcatDir = fun(Prefix, PostFix, Dir) ->
		    Prefix ++ filename:join(Dir, PostFix)
	    end.
SetupUnix = fun(_) ->
		    (false == os:find_executable("sdl-config")) andalso
			Error("Could not find sdl-config~n",[]),
		    SDL_CFLAGS = lists:delete($\n, os:cmd("sdl-config --cflags")),
		    SDL_LIBS   = lists:delete($\n, os:cmd("sdl-config --libs")),
		    %% io:format("CFLAGS ~p~nLIBS ~p~n",[SDL_CFLAGS,SDL_LIBS]),
		    AddConf("SDL_LIBDIR", SDL_LIBS,
			    AddConf("SDL_CFLAGS", SDL_CFLAGS, PortEnv0))
	    end.
%% Windows stuff below
WinType = fun() -> 
		  case GetEnv("TERM") of
		      "msys"   -> mingw;
		      "cygwin" -> cygwin;
		      _ -> windows
		  end
	  end.

WinEnvPrefix = fun(_, mingw)   -> 
		       %% Both cl.exe and gcc.exe accepts Windows paths
		       [Full|_] = string:tokens(os:cmd("where sh"), "\r\n"),
		       filename:dirname(filename:dirname(Full));
		  (cl, cygwin) ->
		       io:format("WARNING: Cygwin not supported~n",[]),
		       os:cmd("cygdrive -m /");
		  (_, _) ->
		       ""
	       end.


InstallSDLDLL = fun(LibDir) ->
			PrivDLL = "priv/SDL.dll",
			io:format("SDL ~p ~p~n", [LibDir, filelib:is_file(PrivDLL)]),
			case filelib:is_file(PrivDLL) of
			    false -> 
				case file:copy(filename:join([LibDir, "SDL.dll"]), PrivDLL) of
				    ok -> ok;
				    _ ->
					case os:find_executable("SDL.dll") of
					    false -> io:format("WARNING: Could not locate SDL.dll~n");
					    Where -> file:copy(Where, PrivDLL)
					end
				end;
			    true -> 
				ok
			end
		end.

WinSetupGCC = fun(SDLDir) ->
		      io:format("Using gcc as compiler~n"),
		      Dir = case filelib:is_dir(SDLDir) of
				true -> SDLDir;
				false -> 
				    EnvPrefix = WinEnvPrefix(gcc, WinType()),
				    ExtDir = EnvPrefix++SDLDir,
				    filelib:is_dir(ExtDir) orelse 
					Error("Could not find SDL dir in:~n"
					      "  ~s~n  ~s~n"
					      " set SDL_DIR=/path/to/opencl", 
					      [SDLDir, ExtDir]),
				    ExtDir
			    end,
		      Inc = ConcatDir("-I", "include", Dir),
		      Type = case erlang:system_info(wordsize) of
				 4 -> "x86";
				 8 -> "x64"
			     end,

		      Lib = case filelib:is_dir(filename:join([Dir, "lib", Type])) of
				true  -> ConcatDir("", filename:join("lib", Type), Dir);
				false -> ConcatDir("", "lib", Dir)
			    end,
		      InstallSDLDLL(Lib),

		      E0 = AddConf("SDL_LIBDIR", "-L" ++ Lib,
				   AddConf("SDL_INCDIR", Inc, PortEnv0)),
		      CC = case GetEnv("CC") of
			       false -> "gcc";
			       EnvCC -> EnvCC
			   end,
		      GCCConf =[{"win32", "CC" , CC},
				{"win32", "DRV_CC_TEMPLATE",
				 "$CC -c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"},
				{"win32", "DRV_LINK_TEMPLATE",
				 "$CC $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS -o $PORT_OUT_FILE"},
				{"win32", "DRV_CFLAGS" , "-g -Wall $ERL_CFLAGS"},
				{"win32", "DRV_LDFLAGS", "-shared $ERL_LDFLAGS"},
				{"win32", "LDFLAGS", "$LDFLAGS $SDL_LIBDIR -lSDL"},
				{"win32", "ERL_LDFLAGS", ""}],
		      Replace = fun(New = {Os, Val, _}, Env) ->
					Add = fun({POs, PVal, _}, {_,Acc}) 
						    when POs =:= Os, Val =:= PVal ->
						      {true, [New|Acc]};
						 (Old,{Found,Acc}) -> {Found, [Old|Acc]}
					      end,
					case lists:foldl(Add, {false,[]}, Env) of
					    {true,  Repl} -> Repl;
					    {false, Repl} -> [New|Repl]
					end
				end,
		      lists:foldl(Replace, E0, GCCConf)
	      end.


Win32VerifyLinkCmd = fun() ->
			     LinkOut = os:cmd("link --version"),
			     case LinkOut of
				 "Microsoft" ++ _ -> ok;
				 _ -> 
				     Dir = filename:dirname(os:find_executable("cl.exe")),
				     Path = os:getenv("PATH"),
				     os:putenv("PATH", filename:nativename(Dir) ++ ";" ++ Path),
				     io:format("WARNING: Wrong link.exe (non Microsoft) found, "
					       "setting cl.exe's dir first in PATH~n", [])
			     end
		     end.

WinSetupCL = fun(SDLDir) ->
		     io:format("Using cl.exe as compiler~n"),
		     Win32VerifyLinkCmd(),
		     Dir = case filelib:is_dir(SDLDir) of
			       true -> SDLDir;
			       false -> 
				   EnvPrefix = WinEnvPrefix(cl, WinType()),
				   ExtDir = EnvPrefix++SDLDir,
				   case filelib:is_dir(ExtDir) of
				       true -> 
					   Error("Could not find SDL dir in:~n"
						 "  ~s~n  ~s~n"
						 " set SDL_DIR=/path/to/opencl", 
						 [SDLDir, ExtDir]),
					   ExtDir
				   end
			   end,
		     Inc = ConcatDir("/I ", "include", Dir),
		     Type = case erlang:system_info(wordsize) of
				4 -> "x86";
				8 -> "x64"
			    end,
		     Lib = ConcatDir("", filename:join("lib",Type), Dir),
		     InstallSDLDLL(Lib),
		     AddConf("SDL_LIBDIR", Lib,
			     AddConf("SDL_INCDIR", Inc, PortEnv0))
	     end.

SetupWin32 = fun(SDLDir) ->
		     %% Do we want to compile with Microsoft's toolchain
		     case os:find_executable("cl.exe") of
			 false ->
			     (os:find_executable("gcc") == false) andalso 
				 Error("No known compiler found",[]),
			     WinSetupGCC(SDLDir);
			 _ ->
			     case GetEnv("CC") of
				 "gcc"     -> WinSetupGCC(SDLDir);
				 "gcc.exe" -> WinSetupGCC(SDLDir);
				 "cc"      -> WinSetupGCC(SDLDir);
				 "cc.exe"  -> WinSetupGCC(SDLDir);
				 false     -> WinSetupCL(SDLDir);
				 Other     -> Error("No known compiler found: CC=~s",[Other])
			     end
		     end
	     end.

WinLocateSDLDir = fun() ->
			  case string:tokens(os:cmd("where SDL.dll"), "\r\n") of
			      [] -> 
				  io:format("Trying to with SDL_DIR=/opt/local/~n"),
				  "/opt/local/";
			      [File|_] ->
				  case filelib:is_file(File) of
				      false -> 
					  Error("Could not find SDL dir:~n"
						" set SDL_DIR=/path/to/opencl or ~n"
						" or add SDL.dll dir to PATH~n",
						[]);
				      true ->
					  filename:dirname(filename:dirname(File))
				  end
			  end
		  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SDLDir = case GetEnv("SDL_DIR") of
	     false ->
		 case os:type() of
		     {unix,  _} -> [];
		     {win32, _} -> WinLocateSDLDir()
		 end;
	     Dir -> Dir
	 end.

PortEnv = case os:type() of
	      {win32, _} -> %% Massage Windows paths
		  SetupWin32(SDLDir);
	      {unix, darwin} ->
		  PortEnv0;
	      {unix, _} ->
		  SetupUnix(SDLDir)
	  end.

[{port_env, PortEnv}|Config0].
