--
-- Installskydb.sql
--
-- Copyright Christophe Gros 2006
-- 
-- The present script is released under the General Public Licence
--

--
-- IsTable(tname)
-- Returns TRUE if table tname exists
-- 

CREATE OR REPLACE FUNCTION IsTable(VARCHAR) RETURNS Boolean AS $$
DECLARE
	Nb INTEGER;
	Na NAME;
BEGIN
	Na := CAST(Lower($1) as NAME);
	SELECT INTO Nb COUNT(relname) FROM pg_class WHERE pg_table_is_visible(oid) AND relname = Na;
	IF Nb = 1 THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;
END;
$$ LANGUAGE 'plpgsql';

--
-- IsFunction(fname)
-- Returns TRUE if function fname exists
--

CREATE OR REPLACE FUNCTION IsFunction(VARCHAR) RETURNS Boolean AS $$
DECLARE
	Nb INTEGER;
	Na NAME;
	Nb INTEGER;
BEGIN
	Na := CAST(Lower($1) as NAME);
	SELECT INTO Nb COUNT(proname) FROM pg_catalog.pg_proc WHERE proname = Na;
	IF Nb = 1 THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;
END;
$$ LANGUAGE 'plpgsql';

--
-- IsField(fieldname, tname)
-- Returns TRUE if table tname contains a field named fieldname
--

CREATE OR REPLACE FUNCTION IsField(VARCHAR, VARCHAR) RETURNS Boolean AS $$
DECLARE
	Fld Name;
	Tbl Name;
	Nb INTEGER;
BEGIN
	Tbl := CAST(Lower($2) AS Name);
	Fld := CAST(Lower($1) AS Name);

	SELECT INTO Nb COUNT(attname) FROM pg_catalog.pg_attribute, pg_catalog.pg_class WHERE pg_catalog.pg_attribute.attrelid = pg_catalog.pg_class.oid AND relname = Tbl AND attname = Fld;
	If Nb = 1 THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;	
END;
$$ LANGUAGE 'plpgsql';

--
-- NewIcon(name, file)
-- insert a new icon name and file if it doesn't exist yet
--

CREATE OR REPLACE FUNCTION NewIcon(VARCHAR, VARCHAR) RETURNS Void AS $$
DECLARE
	Na ALIAS FOR $1;
	Fi ALIAS FOR $2;
	r RECORD;
	
BEGIN
	SELECT INTO r File FROM TypeIcons WHERE File = Fi;
	IF NOT FOUND THEN
		INSERT INTO TypeIcons(Idx, Name, File) VALUES(NEXTVAL('TypeIcons_Idx_Seq'), Na, Fi);
	END IF;
	RETURN;
END;
$$
LANGUAGE 'plpgsql';

--
-- SkylDbInit()
-- The main function for db creation or update
--

CREATE OR REPLACE FUNCTION InitSkyDb() RETURNS Boolean AS $$
DECLARE
	r RECORD;
	n VARCHAR;
BEGIN

--
--	Basic stuff for sk 1.2 and onwards
--
	RAISE NOTICE 'Initial Level (1.2)';
	IF NOT IsTable('Data') THEN
		CREATE TABLE Data -- The Data table
		(
			Idx Int4 PRIMARY KEY, -- Unique Index
			Name VARCHAR   NOT NULL,
			BirthDay DATE  NOT NULL,
			BirthTime TIME NOT NULL,
			Gender CHAR    NOT NULL, -- M = male, F = female, n = neutral
			ChartType Int2 DEFAULT 1, -- 0 = Beginning 1 = End, etc...
			Daylight BOOL  DEFAULT 'f', --  If daylight time saving
			TimeZone NUMERIC(5, 2) DEFAULT '0.0',
			Comment TEXT, -- Free text
			PlaceIdx Int4  NOT NULL -- Place row index
		);
	END IF;
	IF NOT IsTable('Data_Idx_Seq') THEN
		CREATE SEQUENCE Data_Idx_Seq;
	END IF;
	IF NOT IsTable('Data_Name_Key') THEN
		CREATE INDEX Data_Name ON Data (Name) ;
	END IF;
	SELECT INTO r COUNT(*) FROM Data;
	IF r.count = 0 THEN
		--SELECT INTO r Name FROM DATA WHERE Name = 'Default Data';
		--IF r.Name <> 'Default Data' THEN
			INSERT INTO Data (Idx, Name, BirthDay, BirthTime, Gender, ChartType, PlaceIdx)
			VALUES (NEXTVAL('Data_Idx_Seq'), 'Default Data', '1-1-2000', '12:00', 'N', 1, 1);
		--END IF;
	END IF;
	IF NOT IsTable('Place') THEN
		CREATE TABLE Place -- The places table
		(
			Idx Int4 PRIMARY KEY, -- Row index
			Name VARCHAR, -- Place name
			Latitude NUMERIC(5, 2)  NOT NULL, -- Latitude in decimal
			Longitude NUMERIC(5, 2) NOT NULL, -- Longitude in decimal
			CountryIdx Int4 DEFAULT 1 -- Index of relevant country
		);
	END IF;
	IF NOT IsTable('Place_Idx_Seq') THEN
		CREATE SEQUENCE Place_Idx_Seq;
	END IF;
	IF NOT IsTable('Place_Name_Idx') THEN
		CREATE INDEX Place_Name ON Place (Name) ;
	END IF;
	SELECT INTO r COUNT(*) FROM Place;
	IF r.count = 0 THEN
		--SELECT INTO r Name FROM Place WHERE Name = 'Default Place';
		--IF r.Name <> 'Default Place' THEN
			INSERT INTO Place (Idx, Name, Latitude, Longitude, CountryIdx)
			VALUES (NEXTVAL('Place_Idx_Seq'), 'Default Place', 0.0, 0.0, 1) ; -- The default place
		--END IF;
	END IF;
	IF NOT IsTable('Country') THEN
		CREATE TABLE Country -- The countries table
		(
			Idx Int4 PRIMARY KEY, -- Unique index
			Name VARCHAR   NOT NULL, -- Country name
			TZFile VARCHAR NOT NULL -- Zone info file name
		);
	END IF;
	IF NOT IsTable('Country_Idx_Seq') THEN
		CREATE SEQUENCE Country_Idx_Seq;
	END IF;
	IF NOT IsTable('Country_Name_Idx') THEN
		CREATE INDEX Country_Name ON Country (Name) ;
	END IF;
	SELECT INTO r COUNT(*) FROM Country;
	IF r.count = 0 THEN
		--SELECT INTO r Name FROM Country WHERE Name = 'Default Country';
		--IF r.Name <> 'Default Country' THEN
			INSERT INTO Country (Idx, Name, TZFile) VALUES(NEXTVAL('Country_Idx_Seq'), 'Default Country', 'GMT') ;
		--END IF;
	END IF;
	IF NOT IsTable('ObjRest') THEN
		CREATE TABLE ObjRest -- Restrictions data for each object i.e. planet, star, etc...
(
	Index Int4            NOT NULL, -- Restrictions set index
	ObjIndex Int2         NOT NULL, -- Object index
	Rest Bool             NOT NULL, -- If not visible
	OrbAdd  NUMERIC(4, 2) NOT NULL, -- Orb addition
	Influence Int2        NOT NULL -- Power of object
);
	END IF;
	IF NOT IsTable('ObjRest_Index_Idx') THEN
		CREATE INDEX ObjRest_Index ON ObjRest (Index);
	END IF;
	IF NOT IsTable('AspRest') THEN
		CREATE TABLE AspRest -- Restrictions data for each aspect
		(
			Index Int4        NOT NULL, -- Restrictions index
			AspIndex Int2     NOT NULL, -- Aspect index
			Rest Bool         NOT NULL, -- If not visible
			Orb NUMERIC(5, 2) NOT NULL, -- Orb
			Color Int2        NOT NULL, -- Color 0..15
			Influence Int2    NOT NULL -- Power
		);
	END IF;
	IF NOT IsTable('AspRest_Index_Idx') THEN
		CREATE INDEX AspRest_Index ON AspRest (Index);
	END IF;
	IF NOT IsTable('Restrictions') THEN
		CREATE TABLE Restrictions -- The restrictions table
		(
			Idx Int4 PRIMARY KEY, -- Restrictions unique index
			Name VARCHAR   UNIQUE NOT NULL, -- Name
			Houses Bool    NOT NULL, -- If houses used
			Exotic Bool    NOT NULL, -- If Exotic object used e.g. Apollo, etc...
			Stars Bool     NOT NULL, -- If stars used
			Minor Bool     NOT NULL, -- If Minor aspects used .e.g. Sesquiquadrate
			HousesSys Int2 NOT NULL, -- If houses system code .e.g. Placidus, etc...
			TrueNode Bool  NOT NULL, -- If true lunar node
			Sidereal Int2  NOT NULL  -- If Tropical or sidereal zodiac, and which one.
		);
	END IF;
	IF NOT IsTable('Restrictions_Idx_Seq') THEN
		CREATE SEQUENCE Restrictions_Idx_Seq;
	END IF;
	IF NOT IsTable('Restrictions_Name_Idx') THEN
		CREATE INDEX Restrictions_Name ON Restrictions (Name);
	END IF;
	IF NOT IsTable('TypeIcons') THEN
		CREATE TABLE TypeIcons 
		(
			Idx Int4 PRIMARY KEY,
			Name VARCHAR NOT NULL,
			File VARCHAR NOT NULL
		);
	END IF;
	IF NOT IsTable('TypeIcons_Idx_Seq') THEN
		CREATE SEQUENCE TypeIcons_Idx_Seq;
	END IF;
	PERFORM NewIcon('Birth/Beginning', 'beginning.png');
	PERFORM NewIcon('Death/End', 'end.png');
	IF NOT IsTable('VData') THEN
		CREATE TABLE VData -- table containing additional refernce for values storing
		(
			DataIdx Int4 NOT NULL, -- Data row index
			RefIdx  Int4 NOT NULL, -- Original data row index
			RestIdx Int4 NOT NULL, -- Restrictions set index
			Type CHAR NOT NULL -- 'S' = single 'T' = transit 'D' = directions 'P' = progressions 'A' = add angle 'M' = mul by angle
		);
	END IF;
	IF NOT IsTable('VData_Idx_Key') THEN
		CREATE INDEX VData_Idx_Key ON VData(DataIdx);
	END IF;
	IF NOT IsTable('VDV') THEN
		CREATE VIEW VDV AS SELECT * FROM Data, VData WHERE Data.Idx = VData.DataIdx; -- This view displays all the data with saved values
	END IF;
	IF NOT IsTable('Obj') THEN
		CREATE TABLE Obj -- Objects values
		(
			DataIdx Int4 NOT NULL, -- Data row index
			Id CHAR(7) NOT NULL, -- Obj id, e.g. 'Sat' for Saturn
			Lon NUMERIC(8, 3) NOT NULL, -- Longitude in decimal
			Lat NUMERIC(8, 3) NOT NULL, -- latitude in decimal
			Retro BOOL NOT NULL, -- If retro
			InHouse Int2 NOT NULL -- House number
		);
	END IF;
	IF NOT IsTable('Obj_DataIdx_Key') THEN
		CREATE INDEX Obj_DataIdx_Key ON Obj(DataIdx);
	END IF;
	IF NOT IsTable('Asp') THEN
		CREATE TABLE Asp -- Aspects values
		(
			DataIdx Int4 NOT NULL, -- Data row index
			Objs CHAR(15) NOT NULL, -- Objs in aspect .e.g. 'Mar-Ven' for Mars-Venus aspect  
			AspId CHAR(3) NOT NULL, -- Aspect id .e.g. 'Opp' for opposition
			Orb NUMERIC(5, 3) NOT NULL, -- Local orb
			Pos CHAR NOT NULL -- If exact 'E', applying 'A', or separating 'S', aspect
		);
	END IF;
	IF NOT IsTable('Asp_DataIdx_Key') THEN
		CREATE INDEX Asp_DataIdx_Key ON Asp(DataIdx);
	END IF;
--
--	For sk 1.3
--
	RAISE NOTICE 'Level 1.3';
	PERFORM NewIcon('Success', 'up.png');
	PERFORM NewIcon('Failure', 'down.png');
	PERFORM NewIcon('Accident', 'accident.png');
	PERFORM NewIcon('Divorce/Rupture', 'divorce.png');
	PERFORM NewIcon('Solar Revolution', 'solar_rev.png');
	PERFORM NewIcon('Lunar Revolution', 'lunar_rev.png');
	PERFORM NewIcon('Marriage/Association', 'marriage.png');
	PERFORM NewIcon('Ordeal', 'ordeal.png');
	PERFORM NewIcon('Country', 'country.png');
	PERFORM NewIcon('Idea/Intuition', 'idea.png');
	PERFORM NewIcon('Illness', 'illness.png');
	PERFORM Newicon('Publication', 'publication.png');
	PERFORM NewIcon('Journey', 'journey.png');
	IF NOT IsField('S1', 'Restrictions') THEN
		ALTER TABLE Restrictions ADD S1 Int2; -- Score for conj. to Asc/MC;
		ALTER TABLE Restrictions ADD S2 Int2; -- Score for conj. to Dsc/FC/Sun
		ALTER TABLE Restrictions ADD S3 Int2; -- Score for masters of Asc/MC
		ALTER TABLE Restrictions ADD S4 Int2; -- Score for planets in their own sign
		ALTER TABLE Restrictions ADD S5 Int2; -- Score for plan conj. to master of Asc/MC.
		ALTER TABLE Restrictions ADD S6 Int2; -- Score per mastery
		ALTER TABLE Restrictions ADD S7 Int2; -- Score per aspect
		ALTER TABLE Restrictions ADD S8 Int2; -- Score when exalted planet

	END IF;
--
--	For sk 1.4
--
	RAISE NOTICE 'Level 1.4';
	IF NOT IsField('Keywords', 'Data') THEN
		ALTER TABLE Data ADD Keywords VARCHAR;
		ALTER TABLE Data ADD Accuracy Int2;
	END IF;
	IF NOT IsField('Code', 'Country') THEN
		ALTER TABLE Country ADD Code CHAR(3);
	END IF;
	PERFORM NewIcon('Meeting/Appointment', 'meeting.png');
	PERFORM NewIcon('Company', 'company.png');
	PERFORM NewIcon('Fest', 'Fest.png');
	IF NOT IsField('Angle', 'VData') THEN
		ALTER TABLE VData ADD Angle NUMERIC(8, 3);
	END IF;

--
--	For sk 1.5
--
	RAISE NOTICE 'Level 1.5';
	IF NOT IsTable('PlanetsRank') THEN
		CREATE TABLE PlanetsRank -- Stores the planets rank for VData DataIdx
		(
  			DataIdx Int4 NOT NULL, -- VData Index
 			 Planets CHAR(3)[] NOT NULL, -- Planets Ids (0..9)
  			Scores Int2[] NOT NULL -- Scores for planets
		);
	END IF;
	PERFORM NewIcon('Exact Aspect', 'exasp.png');
	PERFORM NewIcon('Direction Change', 'dirch.png');
	PERFORM NewIcon('Sign Change', 'signch.png');
--
--	For sk 1.6
--
	RAISE NOTICE 'Level 1.6';
	IF NOT IsField('Dst', 'Data') THEN
		ALTER TABLE Data ADD Dst NUMERIC(3,2);
		ALTER TABLE Data ALTER Dst SET DEFAULT 0.0;
		UPDATE Data SET Dst = CASE WHEN Daylight='t' THEN 1.0 ELSE 0.0 END;
	END IF;
	PERFORM NewIcon('Planet Rising', 'rising.png');
	PERFORM NewIcon('Eclipse Time', 'eclipse.png');
  RETURN TRUE;
END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION Del_VData () RETURNS TRIGGER AS $$
    BEGIN
    DELETE FROM Obj WHERE DataIdx = OLD.DataIdx;
    DELETE FROM Asp WHERE DataIdx = OLD.DataIdx;
    RETURN OLD;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION Del_Data () RETURNS TRIGGER AS $$
    DECLARE
	r RECORD;
    BEGIN
	SELECT INTO r RefIdx FROM VDV WHERE RefIdx = OLD.Idx AND DataIdx <> RefIdx;
	IF FOUND THEN
		RAISE EXCEPTION 'Cannot delete this data set. It is referenced by other data sets'; 
		RETURN NULL;
	END IF;
	RAISE NOTICE 'DELETE Data Idx = %', OLD.Idx;
	DELETE FROM VData WHERE DataIdx = OLD.Idx;
        RETURN OLD;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION Del_Restrictions() RETURNS TRIGGER AS $$
    BEGIN
	RAISE NOTICE 'DELETE Restrictions Idx = %', OLD.Idx;
	UPDATE VData SET RestIdx = 0 WHERE RestIdx = OLD.Idx;
	RETURN OLD;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION IfSign(Real, Integer) RETURNS Boolean AS $$
    DECLARE
	angle ALIAS FOR $1;
	sn ALIAS FOR $2;
	d INTEGER;
    BEGIN
	d := FLOOR(angle / 30.0) + 1;
	IF d = sn THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION TObj(Int4, VARCHAR, Integer) RETURNS Boolean AS $$
   DECLARE
	r RECORD;
	o ALIAS FOR $2; 
   BEGIN
	SELECT INTO r DataIdx, Lon FROM Obj WHERE DataIdx = $1 AND Id ~* o AND IfSign(Lon, $3);
	IF FOUND THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;
    END;
$$ LANGUAGE 'plpgsql';
 
CREATE OR REPLACE FUNCTION IfAsp(VARCHAR, VARCHAR, VARCHAR) RETURNS Boolean AS $$
    DECLARE
	asp VARCHAR;
	a VARCHAR;
	b VARCHAR;
	per CONSTANT VARCHAR := '%';
	da CONSTANT VARCHAR := '-';
    BEGIN
	asp := rtrim($1, ' ');
	a := rtrim($2, ' ');
	b := rtrim($3, ' ');
	IF a = '' THEN
		IF asp LIKE per || b || per THEN
			RETURN TRUE;
		END IF;
	ELSIF b = '' THEN
		IF asp LIKE per || a || per  THEN
			RETURN TRUE;
		END IF;
	ELSIF asp LIKE (a || da || b) OR asp LIKE (b || da || a) THEN
			RETURN TRUE;
	END IF;
    	RETURN FALSE;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION TAsp(Int4, VARCHAR, VARCHAR, VARCHAR) RETURNS Boolean AS $$
   DECLARE
	r RECORD;
	ai ALIAS FOR $2;
	o1 ALIAS FOR $3;
	o2 ALIAS FOR $4; 
   BEGIN
	SELECT INTO r DataIdx, AspId, Objs FROM Asp WHERE DataIdx = $1 AND AspId ~* ai AND IfAsp(Objs, o1, o2);
	IF FOUND THEN
		RETURN TRUE;
	ELSE
		RETURN FALSE;
	END IF;
    END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION TRetr(Int4, VARCHAR) RETURNS Boolean AS $$
   DECLARE
	ai ALIAS FOR $2;
	r RECORD;
   BEGIN
	SELECT INTO r Id, Retro FROM Obj WHERE DataIdx = $1 AND Id ~* ai;
	IF FOUND THEN 
		RETURN r.Retro;
	END IF;
	RETURN FALSE;
   END;
$$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION THouse(Int4, VARCHAR, Integer) RETURNS Boolean AS $$
   DECLARE
	ai ALIAS FOR $2;
	r RECORD;
   BEGIN
	SELECT INTO r InHouse FROM Obj WHERE DataIdx = $1 AND Id ~* ai;
	IF FOUND THEN
		IF r.InHouse = $3 THEN
			RETURN TRUE;
		END IF;
	END IF;
	RETURN FALSE;
   END;
$$ LANGUAGE 'plpgsql';

SELECT InitSkyDb();

\c skylendar postgres
CREATE GROUP SkyUsers;
\c skylendar skylendar

GRANT SELECT, INSERT, UPDATE, DELETE ON Data, Data_Idx_Seq, Place, Place_Idx_Seq, Country, Country_Idx_Seq, ObjRest, AspRest, Restrictions, Restrictions_Idx_Seq, VData, VDV, Obj, Asp, PlanetsRank TO GROUP SkyUsers;

GRANT SELECT ON TypeIcons, TypeIcons_Idx_Seq TO GROUP SkyUsers;

GRANT EXECUTE ON FUNCTION IfSign(Real, Integer), TObj(Int4, VARCHAR, Integer), IfAsp(VARCHAR, VARCHAR, VARCHAR), TAsp(Int4, VARCHAR, VARCHAR, VARCHAR), TRetr(Int4, VARCHAR), THouse(Int4, VARCHAR, Integer) TO GROUP SkyUsers;


CREATE TRIGGER Del_VData_Trigger BEFORE DELETE ON VData
	FOR EACH ROW EXECUTE PROCEDURE Del_VData();

CREATE TRIGGER Del_Data_Trigger BEFORE DELETE ON Data
	FOR EACH ROW EXECUTE PROCEDURE Del_Data();

CREATE TRIGGER Del_Restrictions_Trigger BEFORE DELETE ON Restrictions
	FOR EACH ROW EXECUTE PROCEDURE Del_Restrictions();

DROP FUNCTION InitSkyDb();
DROP FUNCTION IsField(VARCHAR, VARCHAR);
DROP FUNCTION IsTable(VARCHAR);
DROP FUNCTION IsFunction(VARCHAR);
\c skylendar postgres
ANALYZE;
