content: /*
   * Copyright 2013 Hannes Janetzek
   * Copyright 2016 devemux86
   * Copyright 2019 Gustl22
   *
   * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
   *
   * This program is free software: you can redistribute it and/or modify it under the
   * terms of the GNU Lesser General Public License as published by the Free Software
   * Foundation, either version 3 of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT ANY
   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
   * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License along with
   * this program. If not, see <http://www.gnu.org/licenses/>.
   */
  package org.oscim.renderer;
  
  import org.oscim.backend.AssetAdapter;
  import org.oscim.backend.GL;
  import org.oscim.backend.GLAdapter;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.nio.IntBuffer;
  
  import static org.oscim.backend.GLAdapter.gl;
  
  public abstract class GLShader {
      static final Logger log = LoggerFactory.getLogger(GLShader.class);
  
      public int program;
  
      protected boolean create(String vertexSource, String fragmentSource) {
          return createVersioned(vertexSource, fragmentSource, null);
      }
  
      protected boolean createDirective(String vertexSource, String fragmentSource, String directives) {
          program = createProgramDirective(vertexSource, fragmentSource, directives);
          return program != 0;
      }
  
      protected boolean createVersioned(String vertexSource, String fragmentSource, String version) {
          program = createProgramDirective(vertexSource, fragmentSource, version == null ? null : ("#version " + version + "\n"));
          return program != 0;
      }
  
      protected boolean create(String fileName) {
          return createDirective(fileName, null);
      }
  
      protected boolean createDirective(String fileName, String directives) {
          program = loadShaderDirective(fileName, directives);
          return program != 0;
      }
  
      protected boolean createVersioned(String fileName, String version) {
          program = loadShaderDirective(fileName, version == null ? null : ("#version " + version + "\n"));
          return program != 0;
      }
  
      protected int getAttrib(String name) {
          int loc = gl.getAttribLocation(program, name);
          if (loc < 0)
              log.debug("missing attribute: {}", name);
          return loc;
      }
  
      protected int getUniform(String name) {
          int loc = gl.getUniformLocation(program, name);
          if (loc < 0)
              log.debug("missing uniform: {}", name);
          return loc;
      }
  
      public boolean useProgram() {
          return GLState.useProgram(program);
      }
  
      public static int loadShader(String file) {
          return loadShaderDirective(file, null);
      }
  
      public static int loadShaderDirective(String file, String directives) {
          String path = "shaders/" + file + ".glsl";
          String vs = AssetAdapter.readTextFile(path);
  
          if (vs == null)
              throw new IllegalArgumentException("shader file not found: " + path);
  
          // TODO ...
          int fsStart = vs.indexOf('$');
          if (fsStart < 0 || vs.charAt(fsStart + 1) != '$')
              throw new IllegalArgumentException("not a shader file " + path);
  
          String fs = vs.substring(fsStart + 2);
          vs = vs.substring(0, fsStart);
  
          int shader = createProgramDirective(vs, fs, directives);
          if (shader == 0) {
              System.out.println(vs + " \n\n" + fs);
          }
          return shader;
      }
  
      public static int loadShader(int shaderType, String source) {
  
          int shader = gl.createShader(shaderType);
          if (shader != 0) {
              gl.shaderSource(shader, source);
              gl.compileShader(shader);
              IntBuffer compiled = MapRenderer.getIntBuffer(1);
  
              gl.getShaderiv(shader, GL.COMPILE_STATUS, compiled);
              compiled.position(0);
              if (compiled.get() == 0) {
                  log.error("Could not compile shader " + shaderType + ":");
                  log.error(gl.getShaderInfoLog(shader));
                  gl.deleteShader(shader);
                  shader = 0;
              }
          }
          return shader;
      }
  
      public static int createProgram(String vertexSource, String fragmentSource) {
          return createProgramDirective(vertexSource, fragmentSource, null);
      }
  
      public static int createProgramDirective(String vertexSource, String fragmentSource, String directives) {
          String defs = "";
          if (directives != null)
              defs += directives + "\n";
          if (GLAdapter.GDX_DESKTOP_QUIRKS)
              defs += "#define DESKTOP_QUIRKS 1\n";
          else
              defs += "#define GLES 1\n";
  
          defs += "#define GLVERSION " + (GLAdapter.isGL30() ? "30" : "20") + "\n";
  
          int vertexShader = loadShader(GL.VERTEX_SHADER, defs + vertexSource);
          if (vertexShader == 0) {
              return 0;
          }
  
          int pixelShader = loadShader(GL.FRAGMENT_SHADER, defs + fragmentSource);
          if (pixelShader == 0) {
              return 0;
          }
  
          int program = gl.createProgram();
          if (program != 0) {
              GLUtils.checkGlError(GLShader.class.getName() + ": glCreateProgram");
              gl.attachShader(program, vertexShader);
              GLUtils.checkGlError(GLShader.class.getName() + ": glAttachShader");
              gl.attachShader(program, pixelShader);
              GLUtils.checkGlError(GLShader.class.getName() + ": glAttachShader");
              gl.linkProgram(program);
              IntBuffer linkStatus = MapRenderer.getIntBuffer(1);
              gl.getProgramiv(program, GL.LINK_STATUS, linkStatus);
              linkStatus.position(0);
              if (linkStatus.get() != GL.TRUE) {
                  log.error("Could not link program: ");
                  log.error(gl.getProgramInfoLog(program));
                  gl.deleteProgram(program);
                  program = 0;
              }
          }
          return program;
      }
  }
  