-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
--
-- PostGIS - Spatial Types for PostgreSQL
-- http://postgis.net
--
-- Copyright (C) 2010, 2011 Sandro Santilli <strk@kbt.io>
-- Copyright (C) 2005 Refractions Research Inc.
--
-- This is free software; you can redistribute and/or modify it under
-- the terms of the GNU General Public Licence. See the COPYING file.
--
-- Author: Sandro Santilli <strk@kbt.io>
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

--{
--  CreateTopology(name, SRID, precision, hasZ)
--
-- Create a topology schema, add a topology info record
-- in the topology.topology relation, return it's numeric
-- id.
--
-- Availability: 2.0.0
--
CREATE OR REPLACE FUNCTION topology.CreateTopology(atopology varchar, srid integer, prec float8, hasZ boolean)
RETURNS integer
AS
$BODY$
DECLARE
  rec RECORD;
  topology_id integer;
  sql TEXT;
  zsuffix TEXT := '';
BEGIN

--  FOR rec IN SELECT * FROM pg_namespace WHERE text(nspname) = atopology
--  LOOP
--    RAISE EXCEPTION 'SQL/MM Spatial exception - schema already exists';
--  END LOOP;

  IF hasZ THEN
    zsuffix := 'z';
  END IF;

  IF srid < 0 THEN
    RAISE NOTICE 'SRID value % converted to the officially unknown SRID value 0', srid;
    srid = 0;
  END IF;

  ------ Fetch next id for the new topology
  FOR rec IN SELECT nextval('topology.topology_id_seq')
  LOOP
    topology_id = rec.nextval;
  END LOOP;

  sql := format(
    $$
      CREATE SCHEMA %1$I;

      -------------{ face CREATION
      CREATE TABLE %1$I.face(
        face_id SERIAL,
        mbr GEOMETRY(polygon, %2$L), -- 2d only mbr is good enough
        CONSTRAINT face_primary_key
        PRIMARY KEY(face_id)
      );

      -- Face standard view description
      COMMENT ON TABLE %1$I.face IS
      'Contains face topology primitives';

      -------------} END OF face CREATION

      --------------{ node CREATION

      CREATE TABLE %1$I.node(
        node_id SERIAL,
        containing_face INTEGER,
        geom GEOMETRY(point%3$s, %2$L),
        CONSTRAINT node_primary_key
          PRIMARY KEY(node_id),
        CONSTRAINT face_exists
          FOREIGN KEY(containing_face)
          REFERENCES %1$I.face(face_id)
      );

      -- Node standard view description
      COMMENT ON TABLE %1$I.node IS
      'Contains node topology primitives';

      ------- Add index on containing_face, to speed up
      ------- topology editing (adding/removing faces)
      ------- See http://trac.osgeo.org/postgis/ticket/2861
      CREATE INDEX node_containing_face_idx
        ON %1$I.node (containing_face);

      --------------} END OF node CREATION

      --------------{ edge CREATION

      -- edge_data table
      CREATE TABLE %1$I.edge_data (
        edge_id SERIAL NOT NULL PRIMARY KEY,
        start_node INTEGER NOT NULL,
        end_node INTEGER NOT NULL,
        next_left_edge INTEGER NOT NULL,
        abs_next_left_edge INTEGER NOT NULL,
        next_right_edge INTEGER NOT NULL,
        abs_next_right_edge INTEGER NOT NULL,
        left_face INTEGER NOT NULL,
        right_face INTEGER NOT NULL,
        geom GEOMETRY(linestring%3$s, %2$L) NOT NULL,

        CONSTRAINT start_node_exists
          FOREIGN KEY(start_node)
          REFERENCES %1$I.node(node_id),

        CONSTRAINT end_node_exists
          FOREIGN KEY(end_node)
          REFERENCES %1$I.node(node_id),

        CONSTRAINT left_face_exists
          FOREIGN KEY(left_face)
          REFERENCES %1$I.face(face_id),

        CONSTRAINT right_face_exists
          FOREIGN KEY(right_face)
          REFERENCES %1$I.face(face_id),

        CONSTRAINT next_left_edge_exists
          FOREIGN KEY(abs_next_left_edge)
          REFERENCES %1$I.edge_data(edge_id)
          DEFERRABLE INITIALLY DEFERRED,

        CONSTRAINT next_right_edge_exists
          FOREIGN KEY(abs_next_right_edge)
          REFERENCES %1$I.edge_data(edge_id)
          DEFERRABLE INITIALLY DEFERRED
      );

      -- edge standard view (select rule)
      CREATE VIEW %1$I.edge AS
      SELECT
        edge_id, start_node, end_node, next_left_edge,
        next_right_edge, left_face, right_face, geom
      FROM %1$I.edge_data;

      -- Edge standard view description
      COMMENT ON VIEW %1$I.edge IS
      'Contains edge topology primitives';
      COMMENT ON COLUMN %1$I.edge.edge_id IS
      'Unique identifier of the edge';
      COMMENT ON COLUMN %1$I.edge.start_node IS
      'Unique identifier of the node at the start of the edge';
      COMMENT ON COLUMN %1$I.edge.end_node IS
      'Unique identifier of the node at the end of the edge';
      COMMENT ON COLUMN %1$I.edge.next_left_edge IS
      'Unique identifier of the next edge of the face on the left (when looking in the direction from START_NODE to END_NODE), moving counterclockwise around the face boundary';
      COMMENT ON COLUMN %1$I.edge.next_right_edge IS
      'Unique identifier of the next edge of the face on the right (when looking in the direction from START_NODE to END_NODE), moving counterclockwise around the face boundary';
      COMMENT ON COLUMN %1$I.edge.left_face IS
      'Unique identifier of the face on the left side of the edge when looking in the direction from START_NODE to END_NODE';
      COMMENT ON COLUMN %1$I.edge.right_face IS
      'Unique identifier of the face on the right side of the edge when looking in the direction from START_NODE to END_NODE';
      COMMENT ON COLUMN %1$I.edge.geom IS
      'The geometry of the edge';

      -- edge standard view (insert rule)
      CREATE RULE edge_insert_rule AS
      ON INSERT TO %1$I.edge
      DO INSTEAD INSERT into %1$I.edge_data
      VALUES (
        NEW.edge_id, NEW.start_node, NEW.end_node,
        NEW.next_left_edge, abs(NEW.next_left_edge),
        NEW.next_right_edge, abs(NEW.next_right_edge),
        NEW.left_face, NEW.right_face, NEW.geom
      );

      ------- Add support indices supporting edge linking foreign keys
      ------- See https://trac.osgeo.org/postgis/ticket/2083#comment:20
      CREATE INDEX ON %1$I.edge_data(abs_next_left_edge);
      CREATE INDEX ON %1$I.edge_data(abs_next_right_edge);

      --------------} END OF edge CREATION

      --------------{ layer sequence
      CREATE SEQUENCE %1$I.layer_id_seq;
      --------------} layer sequence

      --------------{ relation CREATION
      CREATE TABLE %1$I.relation (
        topogeo_id integer NOT NULL,
        layer_id integer NOT NULL,
        element_id integer NOT NULL,
        element_type integer NOT NULL,
        UNIQUE(layer_id,topogeo_id,element_id,element_type)
      );
      ------- Add index on element_type, element_id to speed up
      ------- queries looking for TopoGeometries using specific
      ------- primitive elements of the topology
      ------- See http://trac.osgeo.org/postgis/ticket/2083
      CREATE INDEX relation_element_id_idx
        ON %1$I.relation (element_id);

      CREATE TRIGGER relation_integrity_checks
      BEFORE UPDATE OR INSERT ON %1$I.relation
      FOR EACH ROW EXECUTE PROCEDURE
      topology.RelationTrigger(%4$L, %1$L);
      --------------} END OF relation CREATION

      ------- Default (world) face
      INSERT INTO %1$I.face(face_id) VALUES(0);

      ------- GiST index on face
      CREATE INDEX face_gist ON %1$I.face
      USING gist (mbr);

      ------- GiST index on node
      CREATE INDEX node_gist ON %1$I.node
      USING gist (geom);

      ------- GiST index on edge
      CREATE INDEX edge_gist ON %1$I.edge_data
      USING gist (geom);

      ------- Indexes on left_face and right_face of edge_data
      ------- NOTE: these indexes speed up GetFaceGeometry (and thus
      -------       TopoGeometry::Geometry) by a factor of 10 !
      -------       See http://trac.osgeo.org/postgis/ticket/806
      CREATE INDEX edge_left_face_idx
        ON %1$I.edge_data (left_face);
      CREATE INDEX edge_right_face_idx
        ON %1$I.edge_data (right_face);

      ------- Indexes on start_node and end_node of edge_data
      ------- NOTE: this indexes speed up node deletion
      -------       by a factor of 1000 !
      -------       See http://trac.osgeo.org/postgis/ticket/2082
      CREATE INDEX edge_start_node_idx
        ON %1$I.edge_data (start_node);
      CREATE INDEX edge_end_node_idx
        ON %1$I.edge_data (end_node);

      -- TODO: consider also adding an index on node.containing_face

    $$,
    atopology,   -- %1
    srid,        -- %2
    zsuffix,     -- %3
    topology_id  -- %4
  );
#ifdef POSTGIS_TOPOLOGY_DEBUG
  RAISE DEBUG 'SQL: %', sql;
#endif
  EXECUTE sql;

  ------- Add record to the "topology" metadata table
  INSERT INTO topology.topology (id, name, srid, precision, hasZ)
  VALUES (topology_id, atopology, srid, prec, hasZ);

  RETURN topology_id;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE STRICT;

--} CreateTopology

--{ CreateTopology wrappers for unspecified srid or precision or hasZ

--  CreateTopology(name, SRID, precision) -- hasZ = false
-- Availability: 1.1.0
CREATE OR REPLACE FUNCTION topology.CreateTopology(toponame varchar, srid integer, prec float8)
RETURNS integer AS
' SELECT topology.CreateTopology($1, $2, $3, false);'
LANGUAGE 'sql' VOLATILE STRICT;

--  CreateTopology(name, SRID) -- precision = 0
-- Availability: 1.1.0
CREATE OR REPLACE FUNCTION topology.CreateTopology(varchar, integer)
RETURNS integer AS
' SELECT topology.CreateTopology($1, $2, 0); '
LANGUAGE 'sql' VOLATILE STRICT;

--  CreateTopology(name) -- srid = unknown, precision = 0
-- Availability: 1.1.0
CREATE OR REPLACE FUNCTION topology.CreateTopology(varchar)
RETURNS integer AS
$$ SELECT topology.CreateTopology($1, ST_SRID('POINT EMPTY'::geometry), 0); $$
LANGUAGE 'sql' VOLATILE STRICT;

--} CreateTopology

