content: /*
   * Copyright 2014 Hannes Janetzek
   * Copyright 2017 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.bucket;
  
  import org.oscim.backend.GL;
  import org.oscim.core.GeometryBuffer;
  import org.oscim.layers.tile.MapTile;
  import org.oscim.layers.tile.MapTile.TileData;
  import org.oscim.renderer.BufferObject;
  import org.oscim.renderer.MapRenderer;
  import org.oscim.utils.pool.Inlist;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.nio.ShortBuffer;
  
  public class ExtrusionBuckets extends TileData {
      static final Logger log = LoggerFactory.getLogger(ExtrusionBuckets.class);
  
      public ExtrusionBucket buckets;
  
      public boolean compiled;
      public long animTime;
  
      public final int zoomLevel;
  
      public final double x;
      public final double y;
  
      public BufferObject ibo;
      public BufferObject vbo;
  
      public ExtrusionBuckets(MapTile tile) {
          zoomLevel = tile.zoomLevel;
          x = tile.x;
          y = tile.y;
      }
  
      /**
       * Add mesh element to corresponding ExtrusionBucket
       *
       * @param element     the MapElement as mesh
       * @param groundScale the scale of ground
       * @param color       the color of element
       */
      public void addMeshElement(GeometryBuffer element, float groundScale, int color) {
          // Add to bucket which has same color
          for (ExtrusionBucket eb = this.buckets; eb != null; eb = eb.next()) {
              if (eb.getColor() == color) {
                  eb.addMesh(element);
                  return;
              }
          }
  
          // Add to new bucket with different color
          ExtrusionBucket eb = new ExtrusionBucket(0, groundScale, color);
          this.buckets = Inlist.push(this.buckets, eb);
          this.buckets.addMesh(element);
      }
  
      /**
       * Add poly element to corresponding ExtrusionBucket
       *
       * @param element     the MapElement as polygon
       * @param groundScale the scale of ground
       * @param colors      the colors (top, side, side, line) of element
       * @param height      the height of extrusion
       * @param minHeight   the minimum height of extrusion
       */
      public void addPolyElement(GeometryBuffer element, float groundScale, float[] colors, int height, int minHeight) {
          // Add to bucket which has same color
          for (ExtrusionBucket eb = this.buckets; eb != null; eb = eb.next()) {
              if (eb.getColors() == colors) {
                  eb.addPoly(element, height, minHeight);
                  return;
              }
          }
  
          // Add to new bucket with different color
          ExtrusionBucket eb = new ExtrusionBucket(0, groundScale, colors);
          this.buckets = Inlist.push(this.buckets, eb);
          this.buckets.addPoly(element, height, minHeight);
      }
  
      /**
       * Set new ExtrusionBuckets and clear previous.
       */
      public void resetBuckets(ExtrusionBucket el) {
          for (RenderBucket b = buckets; b != null; b = b.next)
              b.clear();
  
          buckets = el;
      }
  
      /**
       * Get root bucket
       */
      public ExtrusionBucket buckets() {
          return buckets;
      }
  
      @Override
      protected void dispose() {
          resetBuckets(null);
  
          if (compiled) {
              ibo = BufferObject.release(ibo);
              vbo = BufferObject.release(vbo);
          }
  
      }
  
      public void prepare() {
          for (RenderBucket b = buckets; b != null; b = b.next)
              b.prepare();
      }
  
      public boolean compile() {
  
          if (buckets == null)
              return false;
  
          int sumIndices = 0;
          int sumVertices = 0;
  
          for (ExtrusionBucket b = buckets; b != null; b = b.next()) {
              sumIndices += b.numIndices;
              sumVertices += b.numVertices;
          }
          if (sumIndices == 0)
              return false;
  
          ShortBuffer vboData = MapRenderer.getShortBuffer(sumVertices * 4);
          ShortBuffer iboData = MapRenderer.getShortBuffer(sumIndices);
  
          for (ExtrusionBucket b = buckets; b != null; b = b.next())
              b.compile(vboData, iboData);
  
          int size = sumIndices * 2;
          if (iboData.position() != sumIndices) {
              int pos = iboData.position();
              log.error("invalid indice size: {} {}", sumIndices, pos);
              size = pos * 2;
          }
          ibo = BufferObject.get(GL.ELEMENT_ARRAY_BUFFER, size);
          ibo.loadBufferData(iboData.flip(), size);
  
          size = sumVertices * 4 * 2;
          if (vboData.position() != sumVertices * 4) {
              int pos = vboData.position();
              log.error("invalid vertex size: {} {}", sumVertices, pos);
              size = pos * 2;
          }
  
          vbo = BufferObject.get(GL.ARRAY_BUFFER, size);
          vbo.loadBufferData(vboData.flip(), size);
  
          compiled = true;
  
          return true;
      }
  
  }
  