-- AREAL --

CREATE OR REPLACE FUNCTION vrevisions_stable.insert_areal(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore,
    in_area double precision,
    in_the_center geometry,
    in_service_attrs hstore DEFAULT ''::hstore)
    RETURNS void AS
$$
DECLARE
    old_branches hstore;
    old_mask_id bigint;
    new_mask_id bigint;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_a
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND commit_id=in_commit_id AND zmin=in_zmin AND zmax=in_zmax
            AND CASE
                WHEN in_service_attrs IS NULL
                THEN service_attrs IS NULL
                ELSE service_attrs=in_service_attrs
                END
        LIMIT 1;
    IF FOUND THEN
        new_mask_id := vrevisions_stable.get_branch_mask_id(old_branches || hstore(in_branch_id::text,'1'));
        UPDATE vrevisions_stable.objects_a
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(hstore(in_branch_id::text,'1'));
        INSERT INTO vrevisions_stable.objects_a (branch_mask_id, id, commit_id, the_geom, zmin, zmax, domain_attrs, service_attrs, area, the_center)
            VALUES (new_mask_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_service_attrs, in_area, in_the_center);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_areal_impl(
    in_branch_id bigint,
    in_id bigint,
    old_mask_id bigint,
    old_branches hstore)
    RETURNS void AS
$$
DECLARE
    new_mask_id bigint;
    new_branches hstore;
BEGIN
    new_branches := old_branches - in_branch_id::text;
    IF new_branches=''::hstore THEN
        DELETE FROM vrevisions_stable.objects_a
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(new_branches);
        UPDATE vrevisions_stable.objects_a
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_areal(
    in_branch_id bigint,
    in_id bigint)
    RETURNS void AS
$$
DECLARE
    old_mask_id bigint;
    old_branches hstore;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_a
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_areal_impl(in_branch_id, in_id, old_mask_id, old_branches);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_areal(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore,
    in_area double precision,
    in_the_center geometry)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_a
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_areal_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_areal(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_area, in_the_center, r.service_attrs);
    ELSE
        PERFORM vrevisions_stable.insert_areal(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_area, in_the_center);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_areal_service_attrs(
    in_branch_id bigint,
    in_id bigint,
    in_zmin integer,
    in_zmax integer,
    in_service_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_a
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_areal_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_areal(
            in_branch_id, in_id, r.commit_id, r.the_geom, in_zmin, in_zmax, r.domain_attrs, r.area, r.the_center, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- LINEAR --

CREATE OR REPLACE FUNCTION vrevisions_stable.insert_linear(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore,
    in_length double precision,
    in_service_attrs hstore DEFAULT ''::hstore)
    RETURNS void AS
$$
DECLARE
    old_branches hstore;
    old_mask_id bigint;
    new_mask_id bigint;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_l
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND commit_id=in_commit_id AND zmin=in_zmin AND zmax=in_zmax
            AND CASE
                WHEN in_service_attrs IS NULL
                THEN service_attrs IS NULL
                ELSE service_attrs=in_service_attrs
                END
        LIMIT 1;
    IF FOUND THEN
        new_mask_id := vrevisions_stable.get_branch_mask_id(old_branches || hstore(in_branch_id::text,'1'));
        UPDATE vrevisions_stable.objects_l
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(hstore(in_branch_id::text,'1'));
        INSERT INTO vrevisions_stable.objects_l (branch_mask_id, id, commit_id, the_geom, zmin, zmax, domain_attrs, service_attrs, length)
            VALUES (new_mask_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_service_attrs, in_length);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_linear_impl(
    in_branch_id bigint,
    in_id bigint,
    old_mask_id bigint,
    old_branches hstore)
    RETURNS void AS
$$
DECLARE
    new_mask_id bigint;
    new_branches hstore;
BEGIN
    new_branches := old_branches - in_branch_id::text;
    IF new_branches=''::hstore THEN
        DELETE FROM vrevisions_stable.objects_l
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(new_branches);
        UPDATE vrevisions_stable.objects_l
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_linear(
    in_branch_id bigint,
    in_id bigint)
    RETURNS void AS
$$
DECLARE
    old_mask_id bigint;
    old_branches hstore;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_l
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_linear_impl(in_branch_id, in_id, old_mask_id, old_branches);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_linear(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore,
    in_length double precision)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_l
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_linear_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_linear(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_length, r.service_attrs);
    ELSE
        PERFORM vrevisions_stable.insert_linear(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_length);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_linear_service_attrs(
    in_branch_id bigint,
    in_id bigint,
    in_zmin integer,
    in_zmax integer,
    in_service_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_l
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_linear_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_linear(
            in_branch_id, in_id, r.commit_id, r.the_geom, in_zmin, in_zmax, r.domain_attrs, r.length, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- POINT --

CREATE OR REPLACE FUNCTION vrevisions_stable.insert_point(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore,
    in_service_attrs hstore DEFAULT ''::hstore)
    RETURNS void AS
$$
DECLARE
    old_branches hstore;
    old_mask_id bigint;
    new_mask_id bigint;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_p
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND commit_id=in_commit_id AND zmin=in_zmin AND zmax=in_zmax
            AND CASE
                WHEN in_service_attrs IS NULL
                THEN service_attrs IS NULL
                ELSE service_attrs=in_service_attrs
                END
        LIMIT 1;
    IF FOUND THEN
        new_mask_id := vrevisions_stable.get_branch_mask_id(old_branches || hstore(in_branch_id::text,'1'));
        UPDATE vrevisions_stable.objects_p
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(hstore(in_branch_id::text,'1'));
        INSERT INTO vrevisions_stable.objects_p (branch_mask_id, id, commit_id, the_geom, zmin, zmax, domain_attrs, service_attrs)
            VALUES (new_mask_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_point_impl(
    in_branch_id bigint,
    in_id bigint,
    old_mask_id bigint,
    old_branches hstore)
    RETURNS void AS
$$
DECLARE
    new_mask_id bigint;
    new_branches hstore;
BEGIN
    new_branches := old_branches - in_branch_id::text;
    IF new_branches=''::hstore THEN
        DELETE FROM vrevisions_stable.objects_p
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(new_branches);
        UPDATE vrevisions_stable.objects_p
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_point(
    in_branch_id bigint,
    in_id bigint)
    RETURNS void AS
$$
DECLARE
    old_mask_id bigint;
    old_branches hstore;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_p
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_point_impl(in_branch_id, in_id, old_mask_id, old_branches);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_point(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_the_geom geometry,
    in_zmin integer,
    in_zmax integer,
    in_domain_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_p
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_point_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_point(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs, r.service_attrs);
    ELSE
        PERFORM vrevisions_stable.insert_point(
            in_branch_id, in_id, in_commit_id, in_the_geom, in_zmin, in_zmax, in_domain_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_point_service_attrs(
    in_branch_id bigint,
    in_id bigint,
    in_zmin integer,
    in_zmax integer,
    in_service_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_p
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_point_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_point(
            in_branch_id, in_id, r.commit_id, r.the_geom, in_zmin, in_zmax, r.domain_attrs, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- COMPLEX --

CREATE OR REPLACE FUNCTION vrevisions_stable.insert_complex(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_domain_attrs hstore,
    in_service_attrs hstore DEFAULT ''::hstore)
    RETURNS void AS
$$
DECLARE
    old_branches hstore;
    old_mask_id bigint;
    new_mask_id bigint;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_c
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND commit_id=in_commit_id
            AND CASE
                WHEN in_service_attrs IS NULL
                THEN service_attrs IS NULL
                ELSE service_attrs=in_service_attrs
                END
        LIMIT 1;
    IF FOUND THEN
        new_mask_id := vrevisions_stable.get_branch_mask_id(old_branches || hstore(in_branch_id::text,'1'));
        UPDATE vrevisions_stable.objects_c
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(hstore(in_branch_id::text,'1'));
        INSERT INTO vrevisions_stable.objects_c (branch_mask_id, id, commit_id, domain_attrs, service_attrs)
            VALUES (new_mask_id, in_id, in_commit_id, in_domain_attrs, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_complex_impl(
    in_branch_id bigint,
    in_id bigint,
    old_mask_id bigint,
    old_branches hstore)
    RETURNS void AS
$$
DECLARE
    new_mask_id bigint;
    new_branches hstore;
BEGIN
    new_branches := old_branches - in_branch_id::text;
    IF new_branches=''::hstore THEN
        DELETE FROM vrevisions_stable.objects_c
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    ELSE
        new_mask_id := vrevisions_stable.get_branch_mask_id(new_branches);
        UPDATE vrevisions_stable.objects_c
            SET branch_mask_id=new_mask_id
            WHERE id=in_id AND branch_mask_id=old_mask_id;
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.delete_complex(
    in_branch_id bigint,
    in_id bigint)
    RETURNS void AS
$$
DECLARE
    old_mask_id bigint;
    old_branches hstore;
BEGIN
    SELECT branch_mask_id, branches INTO old_mask_id, old_branches
        FROM vrevisions_stable.objects_c
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_complex_impl(in_branch_id, in_id, old_mask_id, old_branches);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_complex(
    in_branch_id bigint,
    in_id bigint,
    in_commit_id bigint,
    in_domain_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_c
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_complex_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_complex(
            in_branch_id, in_id, in_commit_id, in_domain_attrs, r.service_attrs);
    ELSE
        PERFORM vrevisions_stable.insert_complex(
            in_branch_id, in_id, in_commit_id, in_domain_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION vrevisions_stable.update_complex_service_attrs(
    in_branch_id bigint,
    in_id bigint,
    in_zmin integer,
    in_zmax integer,
    in_service_attrs hstore)
    RETURNS void AS
$$
DECLARE
    r RECORD;
BEGIN
    SELECT * INTO r
        FROM vrevisions_stable.objects_c
        JOIN vrevisions_stable.branch_mask
        USING (branch_mask_id)
        WHERE id=in_id AND branches ? in_branch_id::text
        LIMIT 1;
    IF FOUND THEN
        PERFORM vrevisions_stable.delete_complex_impl(in_branch_id, in_id, r.branch_mask_id, r.branches);
        PERFORM vrevisions_stable.insert_complex(
            in_branch_id, in_id, r.commit_id, r.domain_attrs, in_service_attrs);
    END IF;
END;
$$ LANGUAGE plpgsql;
