content: /*
   * Copyright 2013 Hannes Janetzek
   * Copyright 2016 devemux86
   *
   * 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.core.MapPosition;
  import org.oscim.core.Tile;
  import org.oscim.renderer.bucket.BitmapBucket;
  import org.oscim.renderer.bucket.CircleBucket;
  import org.oscim.renderer.bucket.HairLineBucket;
  import org.oscim.renderer.bucket.LineBucket;
  import org.oscim.renderer.bucket.LineTexBucket;
  import org.oscim.renderer.bucket.MeshBucket;
  import org.oscim.renderer.bucket.PolygonBucket;
  import org.oscim.renderer.bucket.RenderBucket;
  import org.oscim.renderer.bucket.RenderBuckets;
  import org.oscim.renderer.bucket.TextureBucket;
  import org.oscim.utils.FastMath;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import static org.oscim.renderer.MapRenderer.COORD_SCALE;
  import static org.oscim.renderer.bucket.RenderBucket.BITMAP;
  import static org.oscim.renderer.bucket.RenderBucket.CIRCLE;
  import static org.oscim.renderer.bucket.RenderBucket.HAIRLINE;
  import static org.oscim.renderer.bucket.RenderBucket.LINE;
  import static org.oscim.renderer.bucket.RenderBucket.MESH;
  import static org.oscim.renderer.bucket.RenderBucket.POLYGON;
  import static org.oscim.renderer.bucket.RenderBucket.SYMBOL;
  import static org.oscim.renderer.bucket.RenderBucket.TEXLINE;
  
  /**
   * Base class to use the renderer.elements for drawing.
   * <p/>
   * All methods that modify 'buckets' MUST be synchronized!
   */
  public class BucketRenderer extends LayerRenderer {
  
      public static final Logger log = LoggerFactory.getLogger(BucketRenderer.class);
  
      /**
       * Use mMapPosition.copy(position) to keep the position for which
       * the Overlay is *compiled*. NOTE: required by setMatrix utility
       * functions to draw this layer fixed to the map
       */
      protected MapPosition mMapPosition;
  
      /**
       * Wrap around dateline
       */
      protected boolean mFlipOnDateLine = true;
  
      /**
       * Buckets for rendering
       */
      public final RenderBuckets buckets;
  
      public BucketRenderer() {
          buckets = new RenderBuckets();
          mMapPosition = new MapPosition();
      }
  
      protected boolean mInitialized;
  
      /**
       * Default implementation:
       * Copy initial Viewport position and compile buckets.
       */
      @Override
      public void update(GLViewport v) {
          if (!mInitialized) {
              mMapPosition.copy(v.pos);
              mInitialized = true;
              compile();
          }
      }
  
      /**
       * Render all 'buckets'
       */
      @Override
      public synchronized void render(GLViewport v) {
          MapPosition layerPos = mMapPosition;
  
          GLState.test(false, false);
          GLState.blend(true);
  
          float div = (float) (v.pos.scale / layerPos.scale);
  
          boolean project = true;
  
          setMatrix(v, project);
  
          for (RenderBucket b = buckets.get(); b != null; ) {
  
              buckets.bind();
  
              if (!project && b.type != SYMBOL) {
                  project = true;
                  setMatrix(v, project);
              }
  
              switch (b.type) {
                  case POLYGON:
                      b = PolygonBucket.Renderer.draw(b, v, 1, true);
                      break;
                  case LINE:
                      b = LineBucket.Renderer.draw(b, v, div, buckets);
                      break;
                  case TEXLINE:
                      b = LineTexBucket.Renderer.draw(b,
                              v,
                              div,
                              FastMath.pow(layerPos.zoomLevel - v.pos.zoomLevel) * (float) layerPos.getZoomScale(),
                              buckets);
                      break;
                  case MESH:
                      b = MeshBucket.Renderer.draw(b, v);
                      break;
                  case HAIRLINE:
                      b = HairLineBucket.Renderer.draw(b, v);
                      break;
                  case BITMAP:
                      b = BitmapBucket.Renderer.draw(b, v, 1, 1);
                      break;
                  case SYMBOL:
                      if (project) {
                          project = false;
                          setMatrix(v, project);
                      }
                      b = TextureBucket.Renderer.draw(b, v, div);
                      break;
                  case CIRCLE:
                      b = CircleBucket.Renderer.draw(b, v);
                      break;
                  default:
                      log.error("invalid bucket {}", b.type);
                      b = b.next;
                      break;
              }
          }
      }
  
      /**
       * Compile all buckets into one BufferObject. Sets renderer to be ready
       * when successful. When no data is available (buckets.countVboSize() == 0)
       * then BufferObject will be released and buckets will not be rendered.
       */
      protected synchronized void compile() {
          boolean ok = buckets.compile(true);
          setReady(ok);
      }
  
      /**
       * Utility: Set matrices.mvp matrix relative to the difference of current
       * MapPosition and the last updated Overlay MapPosition.
       * Use this to 'stick' your layer to the map. Note: Vertex coordinates
       * are assumed to be scaled by MapRenderer.COORD_SCALE (== 8).
       *
       * @param v       GLViewport
       * @param project if true apply view- and projection, or just view otherwise.
       */
      protected void setMatrix(GLViewport v, boolean project) {
          setMatrix(v, project, COORD_SCALE);
      }
  
      protected void setMatrix(GLViewport v, boolean project, float coordScale) {
          setMatrix(v.mvp, v, project, coordScale);
      }
  
      protected void setMatrix(GLMatrix mvp, GLViewport v, boolean project, float coordScale) {
          MapPosition oPos = mMapPosition;
  
          double tileScale = Tile.SIZE * v.pos.scale;
  
          double x = oPos.x - v.pos.x;
          double y = oPos.y - v.pos.y;
  
          if (mFlipOnDateLine) {
              //wrap around date-line
              while (x < 0.5)
                  x += 1.0;
              while (x > 0.5)
                  x -= 1.0;
          }
  
          mvp.setTransScale((float) (x * tileScale),
                  (float) (y * tileScale),
                  (float) (v.pos.scale / oPos.scale) / coordScale);
  
          mvp.multiplyLhs(project ? v.viewproj : v.view);
      }
  
      /**
       * Utility: Set matrices.mvp matrix relative to the difference of current
       * MapPosition and the last updated Overlay MapPosition and applies
       * view-projection-matrix.
       */
      protected void setMatrix(GLViewport v) {
          setMatrix(v, true);
      }
  }
  