-- packages/acs-events/sql/acs-events-create.sql
--
-- @author smeeks@arsdigita.com
-- @creation-date 2000-11-16
-- @cvs-id $Id: upgrade-0.3d-0.4d.sql,v 1.1.4.1 2005/06/21 19:22:13 andrewg Exp $

create or replace package acs_event
as 
    function new ( 
        -- Creates a new event (20.10.10)
        -- @author W. Scott Meeks
        -- @param event_id          optional id to use for new event
        -- @param name                  optional Name of the new event
        -- @param description   optional Description of the new event
        -- @param html_p        optional Description is html
        -- @param status_summary    optional status information to add to name
        -- @param timespan_id       optional initial time interval set
        -- @param activity_id       optional initial activity
        -- @param recurrence_id     optional id of recurrence information
        -- @param object_type       'acs_event'
        -- @param creation_date     default sysdate
        -- @param creation_user     acs_object param
        -- @param creation_ip       acs_object param
        -- @param context_id        acs_object param
        -- @return The id of the new event.
        --
        event_id        in acs_events.event_id%TYPE default null, 
        name            in acs_events.name%TYPE default null,
        description     in acs_events.description%TYPE default null,
        html_p          in acs_events.html_p%TYPE default null,
        status_summary  in acs_events.status_summary%TYPE default null,
        timespan_id     in acs_events.timespan_id%TYPE default null, 
        activity_id     in acs_events.activity_id%TYPE default null, 
        recurrence_id   in acs_events.recurrence_id%TYPE default null, 
        object_type     in acs_object_types.object_type%TYPE default 'acs_event', 
        creation_date   in acs_objects.creation_date%TYPE default sysdate, 
        creation_user   in acs_objects.creation_user%TYPE default null, 
        creation_ip     in acs_objects.creation_ip%TYPE default null, 
        context_id      in acs_objects.context_id%TYPE default null 
    ) return acs_events.event_id%TYPE; 

    procedure del ( 
        -- Deletes an event (20.10.40)
        -- Also deletes party mappings (via on delete cascade).
        -- If this is the last instance of a recurring event, the recurrence
        -- info is deleted as well
        -- @author W. Scott Meeks
        -- @param event_id id of event to delete
        --
        event_id        in acs_events.event_id%TYPE 
    ); 

    procedure delete_all (
        -- Deletes all instances of an event.  
        -- @author W. Scott Meeks
        -- @param event_id  All events with the same recurrence_id as this one will be deleted.
        --
        event_id in acs_events.event_id%TYPE
    );

    procedure delete_all_recurrences (
        -- Deletes all instances of an event.  
        -- @author W. Scott Meeks
        -- @param recurrence_id All events with this recurrence_id will be deleted.
        --
        recurrence_id in recurrences.recurrence_id%TYPE default null
    );

    function get_name (
        -- Returns the name or the name of the activity associated with the event if 
        -- name is null.
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to get name for
        --
        event_id            in acs_events.event_id%TYPE 
    ) return acs_events.name%TYPE; 

    function get_description (
        -- Returns the description or the description of the activity associated 
        -- with the event if description is null.
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to get description for
        --
        event_id            in acs_events.event_id%TYPE 
    ) return acs_events.description%TYPE; 

    function get_html_p (
        -- Returns html_p or html_p of the activity associated with the event if 
        -- html_p is null.
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to get html_p for
        --
        event_id            in acs_events.event_id%TYPE 
    ) return acs_events.html_p%TYPE; 

    function get_status_summary (
        -- Returns status_summary or status_summary of the activity associated with the event if 
        -- status_summary is null.
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to get status_summary for
        --
        event_id            in acs_events.event_id%TYPE 
    ) return acs_events.status_summary%TYPE; 

    procedure timespan_set (
        -- Sets the time span for an event (20.10.15)
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to update
        -- @param timespan_id   new time interval set
        --
        event_id        in acs_events.event_id%TYPE,
        timespan_id     in timespans.timespan_id%TYPE
    );

    procedure recurrence_timespan_edit (
        event_id               in acs_events.event_id%TYPE,
        start_date             in time_intervals.start_date%TYPE,
        end_date               in time_intervals.end_date%TYPE
    );

    procedure activity_set (
        -- Sets the activity for an event (20.10.20)
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to update
        -- @param activity_id           new activity
        --
        event_id        in acs_events.event_id%TYPE,
        activity_id     in acs_activities.activity_id%TYPE
    );

    procedure party_map (
        -- Adds a party mapping to an event (20.10.30)
        -- @author W. Scott Meeks
        -- @param event_id event to add mapping to
        -- @param party_id party to add mapping for
        --
        event_id        in acs_events.event_id%TYPE,
        party_id        in parties.party_id%TYPE
    );

    procedure party_unmap (
        -- Deletes a party mapping from an event (20.10.30)
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to delete mapping from
        -- @param party_id                      id of party to delete mapping for
        --
        event_id    in acs_events.event_id%TYPE,
        party_id    in parties.party_id%TYPE
    );

    function recurs_p (
        -- Returns 't' if event recurs, 'f' otherwise (20.50.40)
        -- @author W. Scott Meeks
        -- @param event_id                      id of event to check
        -- @return 't' or 'f'
        --
        event_id    in acs_events.event_id%TYPE
    ) return char;

    function instances_exist_p (
        -- Returns 't' if events with the given recurrence_id exist, 'f' otherwise
        -- @author W. Scott Meeks
        -- @param recurrence_id                 id of recurrence to check
        -- @return 't' or 'f'
        --
        recurrence_id   in acs_events.recurrence_id%TYPE
    ) return char;

    procedure insert_instances (
        -- This is the key procedure creating recurring events.  This procedure
        -- uses the interval set and recurrence information referenced by the event
        -- to insert additional information to represent the recurrences.   
        -- Events will be added up until the earlier of recur_until and
        -- cutoff_date.  The procedure enforces a hard internal 
        -- limit of adding no more than 10,000 recurrences at once to reduce the 
        -- risk of demolishing the DB because of application bugs.  The date of the
        -- last recurrence added is marked as the db_populated_until date.
        --
        -- The application is responsible for calling this function again if 
        -- necessary to populate to a later date.  
        --
        -- @author W. Scott Meeks
        -- @param event_id              The id of the event to recur.  If the 
        --                              event's recurrence_id is null, nothing happens.
        -- @param cutoff_date           optional If provided, determines how far out to
        --                              prepopulate the DB.  If not provided, then 
        --                              defaults to sysdate plus the value of the
        --                              EventFutureLimit site parameter.
        event_id        in acs_events.event_id%TYPE, 
        cutoff_date     in date default null
  );

  procedure shift (
        -- Shifts the timespan of an event by the given offsets.
        -- @author W. Scott Meeks
        -- @param event_id              Event to shift.
        -- @param start_offset  optional If provided, adds this number to the
        --                                              start_dates of the timespan of the event.
        --                                              No effect on any null start_date.
        -- @param end_offset    optional If provided, adds this number to the
        --                                              end_dates of the timespan of the event.
        --                                              No effect on any null end_date.
        --
        event_id        in acs_events.event_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
  );

  procedure shift_all (
        -- Shifts the timespan of all instances of a recurring event
        -- by the given offsets.
        -- @author W. Scott Meeks
        -- @param event_id      All events with the same
        --                          recurrence_id as this one will be shifted.
        -- @param start_offset  optional If provided, adds this number to the
        --                          start_dates of the timespan of the event
        --                          instances.  No effect on any null start_date.
        -- @param end_offset    optional If provided, adds this number to the
        --                          end_dates of the timespan of the event
        --                          instances.  No effect on any null end_date.
        --
        event_id        in acs_events.event_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
  );

  procedure shift_all (
        -- Same as above but invoked using recurrence Id
        recurrence_id   in recurrences.recurrence_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
  );

end acs_event; 
/ 
show errors

-- packages/acs-events/sql/activity-create.sql
--
-- @author W. Scott Meeks
-- @author Gary Jin (gjin@arsdigita.com)
-- $Id: upgrade-0.3d-0.4d.sql,v 1.1.4.1 2005/06/21 19:22:13 andrewg Exp $
--
-- The activity object


create or replace package acs_activity
as
    function new ( 
         -- Create a new activity
         -- @author W. Scott Meeks
         -- @param activity_id       optional id to use for new activity
         -- @param name                         Name of the activity 
         -- @param description          optional description of the activity
         -- @param html_p               optional description is html
         -- @param status_summary       optional additional status to display
         -- @param object_type          'acs_activity'
         -- @param creation_date        default sysdate
         -- @param creation_user        acs_object param
         -- @param creation_ip          acs_object param
         -- @param context_id           acs_object param
         -- @return The id of the new activity.
         --
         activity_id         in acs_activities.activity_id%TYPE   default null, 
         name                in acs_activities.name%TYPE,
         description         in acs_activities.description%TYPE   default null,
         html_p              in acs_activities.html_p%TYPE        default 'f',
         status_summary      in acs_activities.status_summary%TYPE        default null,
         object_type         in acs_object_types.object_type%TYPE default 'acs_activity', 
         creation_date       in acs_objects.creation_date%TYPE    default sysdate, 
         creation_user       in acs_objects.creation_user%TYPE    default null, 
         creation_ip         in acs_objects.creation_ip%TYPE      default null, 
         context_id          in acs_objects.context_id%TYPE       default null 
    ) return acs_activities.activity_id%TYPE; 

    function name (
        -- name method
        -- @author gjin@arsdigita.com
        -- @param activity_id
        --
        activity_id          in acs_activities.activity_id%TYPE
        
    ) return acs_activities.name%TYPE;
 
    procedure del ( 
         -- Deletes an activity
         -- @author W. Scott Meeks
         -- @param activity_id      id of activity to delete
         activity_id      in acs_activities.activity_id%TYPE 
    ); 


    -- NOTE: can't use update

    procedure edit (
         -- Update the name or description of an activity
         -- @author W. Scott Meeks
         -- @param activity_id activity to update
         -- @param name        optional New name for this activity
         -- @param description optional New description for this activity
         -- @param html_p      optional New value of html_p for this activity
         activity_id         in acs_activities.activity_id%TYPE, 
         name                in acs_activities.name%TYPE default null,
         description         in acs_activities.description%TYPE default null,
         html_p              in acs_activities.html_p%TYPE default null,
         status_summary      in acs_activities.status_summary%TYPE default null
    );

    procedure object_map (
         -- Adds an object mapping to an activity
         -- @author W. Scott Meeks
         -- @param activity_id       id of activity to add mapping to
         -- @param object_id         id of object to add mapping for
         --
         activity_id         in acs_activities.activity_id%TYPE, 
         object_id           in acs_objects.object_id%TYPE
    );

    procedure object_unmap (
         -- Deletes an object mapping from an activity
         -- @author W. Scott Meeks
         -- @param activity_id activity to delete mapping from
         -- @param object_id   object to delete mapping for
         --
         activity_id         in acs_activities.activity_id%TYPE, 
         object_id           in acs_objects.object_id%TYPE
    );

end acs_activity;
/
show errors


-- packages/acs-events/sql/recurrence-create.sql
--
-- Support for temporal recurrences
--
-- $Id: upgrade-0.3d-0.4d.sql,v 1.1.4.1 2005/06/21 19:22:13 andrewg Exp $

-- These columns describe how an event recurs.  The are modeled on the Palm DateBook.
-- The interval_type 'custom' indicates that the PL/SQL function referenced in
-- custom_func should be used to generate the recurrences.

create or replace package recurrence
as
    function new (
         -- Creates a new recurrence
         -- @author W. Scott Meeks
         -- @param interval_type        Sets interval_type of new recurrence
         -- @param every_nth_interval   Sets every_nth_interval of new recurrence
         -- @param days_of_week         optional If provided, sets days_of_week
         --                                  of new recurrence
         -- @param recur_until          optional If provided, sets recur_until
         --                                  of new recurrence
         -- @param custom_func          optional If provided, set name of 
         --                                  custom recurrence function
         -- @return id of new recurrence
         --
         interval_type          in recurrence_interval_types.interval_name%TYPE,
         every_nth_interval     in recurrences.every_nth_interval%TYPE,
         days_of_week           in recurrences.days_of_week%TYPE default null,
         recur_until            in recurrences.recur_until%TYPE default null,
         custom_func            in recurrences.custom_func%TYPE default null
    ) return recurrences.recurrence_id%TYPE;

    procedure del (
         -- Deletes the recurrence
         -- @author W. Scott Meeks
         -- @param recurrence_id id of recurrence to delete
         --
         recurrence_id          in recurrences.recurrence_id%TYPE
    );
  
end recurrence;
/
show errors


-- packages/acs-events/sql/timespan-create.sql
--

create or replace package time_interval
as
    function new (
         -- Creates a new time interval
         -- @author W. Scott Meeks
         -- @param start_date   optional Sets this as start_date of new interval
         -- @param end_date             optional Sets this as end_date of new interval
         -- @return id of new time interval
         --
         start_date     in time_intervals.start_date%TYPE default null,
         end_date       in time_intervals.end_date%TYPE default null
    ) return time_intervals.interval_id%TYPE;

    procedure del (
         -- Deletes the given time interval
         -- @author W. Scott Meeks
         -- @param interval_id  id of the interval to delete
         --
         interval_id in time_intervals.interval_id%TYPE
    );

    -- NOTE: update is reserved and cannot be used for PL/SQL procedure names

    procedure edit (
         -- Updates the start_date or end_date of an interval
         -- @author W. Scott Meeks
         -- @param interval_id  id of the interval to update
         -- @param start_date   optional If provided, sets this as the new 
         --                         start_date of the interval.
         -- @param end_date     optional If provided, sets this as the new 
         --                         start_date of the interval.
         --
         interval_id    in time_intervals.interval_id%TYPE,
         start_date     in time_intervals.start_date%TYPE default null,
         end_date       in time_intervals.end_date%TYPE default null
    );

    procedure shift (
         -- Updates the start_date or end_date of an interval based on offsets of
         -- fractional days.
         -- @author W. Scott Meeks
         -- @param interval_id  The interval to update.
         -- @param start_offset optional If provided, adds this number to the
         --                              start_date of the interval.  No effect if 
         --                              start_date is null.
         -- @param end_offset   optional If provided, adds this number to the
         --                              end_date of the interval.  No effect if 
         --                              end_date is null.
         --
         interval_id    in time_intervals.interval_id%TYPE,
         start_offset   in number default 0,
         end_offset     in number default 0
    );
        
    function overlaps_p (
        -- Returns 't' if the two intervals overlap, 'f' otherwise.
        -- @author W. Scott Meeks
        -- @param interval_1_id
        -- @param interval_2_id
        -- @return 't' or 'f'
        --
        interval_1_id   in time_intervals.interval_id%TYPE,
        interval_2_id   in time_intervals.interval_id%TYPE
    ) return char;

    function overlaps_p (
        -- Returns 't if the interval bounded by the given start_date or
        -- end_date overlaps the given interval, 'f' otherwise.
        -- @author W. Scott Meeks
        -- @param start_date    optional If provided, see if it overlaps 
        --                                              the interval.
        -- @param end_date              optional If provided, see if it overlaps 
        --                                              the interval.
        -- @return 't' or 'f'
        --
        interval_id     in time_intervals.interval_id%TYPE,
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return char;

    function overlaps_p (
        start_1 in time_intervals.start_date%TYPE,
        end_1   in time_intervals.end_date%TYPE,
        start_2 in time_intervals.start_date%TYPE,
        end_2   in time_intervals.end_date%TYPE
    ) return char;

    function eq (
        -- Checks if two intervals are equivalent
        -- @author W. Scott Meeks
        -- @param interval_1_id First interval
        -- @param interval_2_id Second interval
        -- @return boolean
        --
        interval_1_id   in time_intervals.interval_id%TYPE,
        interval_2_id   in time_intervals.interval_id%TYPE
    ) return boolean;

    function copy (
        -- Creates a new copy of a time interval, offset by optional offset
        -- @author W. Scott Meeks
        -- @param interval_id   Interval to copy
        -- @param offset        optional If provided, interval is
        --                      offset by this number of days.
        -- @return interval_id
        --
        interval_id     in time_intervals.interval_id%TYPE,
        offset          in integer default 0
    ) return time_intervals.interval_id%TYPE;

end time_interval;
/
show errors

create or replace package timespan
as
    function new (
        -- Creates a new timespan (20.20.10)
        -- given a time_interval
        -- Copies the interval so the caller is responsible for deleting it
        interval_id in time_intervals.interval_id%TYPE default null
    ) return timespans.timespan_id%TYPE;

    function new (
        -- Creates a new timespan (20.20.10)
        -- given a start_date and end_date
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return timespans.timespan_id%TYPE;

    procedure del (
        -- Deletes the timespan and any contained intervals 
        -- @author W. Scott Meeks
        -- @param timespan_id   id of timespan to delete
        timespan_id in timespans.timespan_id%TYPE
    );

    -- Join a new timespan or time interval to an existing timespan

    procedure join (
        -- timespan_1_id is modified, timespan_2_id is not
        timespan_1_id   in timespans.timespan_id%TYPE,
        timespan_2_id   in timespans.timespan_id%TYPE
    );

        -- Unfortunately, Oracle can't distinguish the signature of this function
        -- with the previous because the args have the same underlying types
        -- 
    procedure join_interval (
        -- interval is copied to the timespan
        timespan_id       in timespans.timespan_id%TYPE,
        interval_id       in time_intervals.interval_id%TYPE,
        copy_p                    in boolean default true
    );

    procedure join (
        timespan_id       in timespans.timespan_id%TYPE,
        start_date        in time_intervals.start_date%TYPE default null,
        end_date          in time_intervals.end_date%TYPE default null
    );


    procedure interval_delete (
        -- Deletes an interval from the given timespan
        -- @author W. Scott Meeks
        -- @param timespan_id   timespan to delete from
        -- @param interval_id           delete this interval from the set
        --
        timespan_id     in timespans.timespan_id%TYPE,
        interval_id     in time_intervals.interval_id%TYPE
    );

    -- Information

    function exists_p (
        -- If its contained intervals are all deleted, then a timespan will
        -- automatically be deleted.  This checks a timespan_id to make sure it's 
        -- still valid.
        -- @author W. Scott Meeks
        -- @param timespan_id   id of timespan to check
        -- @return 't' or 'f'
        timespan_id in timespans.timespan_id%TYPE
    ) return char;

    function multi_interval_p (
        -- Returns 't' if timespan contains more than one interval, 
        -- 'f' otherwise (
        -- @author W. Scott Meeks
        -- @param timespan_id   id of set to check
        -- @return 't' or 'f'
        timespan_id in timespans.timespan_id%TYPE
    ) return char;


    function overlaps_p (
        -- Checks to see if a given interval overlaps any of the intervals
        -- in the given timespan.
        timespan_1_id   in timespans.timespan_id%TYPE,
        timespan_2_id   in timespans.timespan_id%TYPE
    ) return char;

    -- Unfortunately, Oracle can't distinguish the signature of this function
    -- with the previous because the args have the same underlying types
    -- 
    function overlaps_interval_p (
        timespan_id     in timespans.timespan_id%TYPE,
        interval_id     in time_intervals.interval_id%TYPE default null
    ) return char;

    function overlaps_p (
        timespan_id     in timespans.timespan_id%TYPE,
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return char;

    function copy (
        -- Creates a new copy of a timespan, offset by optional offset
        -- @author W. Scott Meeks
        -- @param timespan_id   Timespan to copy
        -- @param offset        optional If provided, all dates in timespan
        --                      are offset by this number of days.
        -- @return timespan_id
        --
        timespan_id     in timespans.timespan_id%TYPE,
        offset          in integer default 0
    ) return timespans.timespan_id%TYPE;

end timespan;
/
show errors

create or replace package body acs_event
as 
    function new ( 
        event_id        in acs_events.event_id%TYPE default null, 
        name            in acs_events.name%TYPE default null,
        description     in acs_events.description%TYPE default null,
        html_p          in acs_events.html_p%TYPE default null,
        status_summary  in acs_events.status_summary%TYPE default null,
        timespan_id     in acs_events.timespan_id%TYPE default null, 
        activity_id     in acs_events.activity_id%TYPE default null, 
        recurrence_id   in acs_events.recurrence_id%TYPE default null, 
        object_type     in acs_object_types.object_type%TYPE default 'acs_event', 
        creation_date   in acs_objects.creation_date%TYPE default sysdate, 
        creation_user   in acs_objects.creation_user%TYPE default null, 
        creation_ip     in acs_objects.creation_ip%TYPE default null, 
        context_id      in acs_objects.context_id%TYPE default null 
    ) return acs_events.event_id%TYPE
    is
        new_event_id acs_events.event_id%TYPE;
    begin
        new_event_id := acs_object.new(
            object_id => event_id,
            object_type => object_type,
            creation_date => creation_date,
            creation_user => creation_user,
            creation_ip => creation_ip,
            context_id => context_id
        );
                
        insert into acs_events
            (event_id, name, description, html_p, status_summary, activity_id, timespan_id, recurrence_id)
        values
            (new_event_id, name, description, html_p, status_summary, activity_id, timespan_id, recurrence_id);

        return new_event_id;
    end new; 

    procedure del ( 
        event_id in acs_events.event_id%TYPE 
    )
    is
        recurrence_id acs_events.recurrence_id%TYPE;
    begin
        select recurrence_id into recurrence_id
        from   acs_events
        where  event_id = acs_event.del.event_id;

        -- acs_events and acs_event_party_map deleted via on delete cascade
        acs_object.del(event_id); 

        -- Check for no more instances and delete recurrence if exists
        if instances_exist_p(recurrence_id) = 'f' then
            recurrence.del(recurrence_id);
        end if;
    end del;

    procedure delete_all (
        event_id in acs_events.event_id%TYPE
    )
    is
        recurrence_id acs_events.recurrence_id%TYPE;
    begin

        select recurrence_id into recurrence_id
        from   acs_events
        where  event_id = delete_all.event_id;

        delete_all_recurrences(recurrence_id);
    end delete_all;

    procedure delete_all_recurrences (
        recurrence_id   in recurrences.recurrence_id%TYPE default null
    )
    is
        cursor event_id_cursor is
            select event_id
            from   acs_events
            where  recurrence_id = delete_all_recurrences.recurrence_id;
        event_id event_id_cursor%ROWTYPE;
    begin
        if recurrence_id is not null then
            for event_id in event_id_cursor loop
                acs_event.del(event_id.event_id);
            end loop;
        end if;
    end delete_all_recurrences;

    -- Equivalent functionality to get_name and get_description provided by
    -- acs_event_activity view

    function get_name (
        event_id in acs_events.event_id%TYPE 
    ) return acs_events.name%TYPE
    is
        name acs_events.name%TYPE; 
    begin
        select nvl(e.name, a.name) into name
        from   acs_events e, 
               acs_activities a
        where  event_id = get_name.event_id
        and    e.activity_id = a.activity_id(+);

        return name;
    end get_name;

    function get_description (
        event_id    in acs_events.event_id%TYPE 
    ) return acs_events.description%TYPE
    is
        description acs_events.description%TYPE; 
    begin
        select nvl(e.description, a.description) into description
        from   acs_events e, acs_activities a
        where  event_id      = get_description.event_id
        and    e.activity_id = a.activity_id(+);

        return description;
    end get_description;

    function get_html_p (
        event_id    in acs_events.event_id%TYPE 
    ) return acs_events.html_p%TYPE
    is
        html_p acs_events.html_p%TYPE; 
    begin
        select nvl(e.html_p, a.html_p) into html_p
        from  acs_events e, acs_activities a
        where event_id      = get_html_p.event_id
        and   e.activity_id = a.activity_id(+);

        return html_p;
    end get_html_p;

    function get_status_summary (
        event_id    in acs_events.event_id%TYPE 
    ) return acs_events.status_summary%TYPE
    is
        status_summary acs_events.status_summary%TYPE; 
    begin
        select nvl(e.status_summary, a.status_summary) into status_summary
        from  acs_events e, acs_activities a
        where event_id      = get_status_summary.event_id
        and   e.activity_id = a.activity_id(+);

        return status_summary;
    end get_status_summary;

    procedure timespan_set (
        event_id        in acs_events.event_id%TYPE,
        timespan_id     in timespans.timespan_id%TYPE
    )
    is
    begin
        update acs_events
        set    timespan_id = timespan_set.timespan_id
        where  event_id    = timespan_set.event_id;
    end timespan_set;

    procedure recurrence_timespan_edit (
        event_id               in acs_events.event_id%TYPE,
        start_date             in time_intervals.start_date%TYPE,
        end_date               in time_intervals.end_date%TYPE
    )
    is
        v_timespan           timespans%ROWTYPE;
        v_one_start_date     time_intervals.start_date%TYPE;
        v_one_end_date       time_intervals.end_date%TYPE;
    begin
        -- get the initial offsets
        select start_date, end_date into v_one_start_date, v_one_end_date
        from time_intervals, timespans, acs_events
        where time_intervals.interval_id = timespans.interval_id and
        timespans.timespan_id = acs_events.timespan_id and
        event_id= recurrence_timespan_edit.event_id;

        for v_timespan in 
        (select * from time_intervals where interval_id in (select interval_id from timespans where timespan_id in (select timespan_id from acs_events where recurrence_id = (select recurrence_id from acs_events where event_id = recurrence_timespan_edit.event_id))))
        LOOP
                time_interval.edit(v_timespan.interval_id, v_timespan.start_date + (start_date - v_one_start_date), v_timespan.end_date + (end_date - v_one_end_date));
        END LOOP;
    end recurrence_timespan_edit;

    procedure activity_set (
        event_id        in acs_events.event_id%TYPE,
        activity_id     in acs_activities.activity_id%TYPE
    )
    as
    begin
        update acs_events
        set    activity_id = activity_set.activity_id
        where  event_id    = activity_set.event_id;
    end activity_set;

    procedure party_map (
        event_id        in acs_events.event_id%TYPE,
        party_id        in parties.party_id%TYPE
    )
    is
    begin
        insert into acs_event_party_map
            (event_id, party_id)
        values
            (event_id, party_id);
    end party_map;

    procedure party_unmap (
        event_id        in acs_events.event_id%TYPE,
        party_id        in parties.party_id%TYPE
    )
    is
    begin
        delete from acs_event_party_map
        where  event_id = party_unmap.event_id
        and    party_id = party_unmap.party_id;
    end party_unmap;

    function recurs_p (
        event_id        in acs_events.event_id%TYPE
    ) return char
    is
        result char;
    begin
        select decode(recurrence_id, null, 'f', 't') into result
        from   acs_events
        where  event_id = recurs_p.event_id;

        return result;
    end recurs_p;

    function instances_exist_p (
        recurrence_id   in acs_events.recurrence_id%TYPE
    ) return char
    is
        result char;
    begin
        -- Only need to check if any rows exist.
        select count(*) into result
        from   dual 
        where exists (select recurrence_id
                      from   acs_events
                      where  recurrence_id = instances_exist_p.recurrence_id);

        if result = 0 then
            return 'f';
        else
            return 't';
        end if;
    end instances_exist_p;

    -- This function is used internally by insert_instances
    function get_value (
        parameter_name in apm_parameters.parameter_name%TYPE
    ) return apm_parameter_values.attr_value%TYPE
    is
        package_id apm_packages.package_id%TYPE;
    begin
        select package_id into package_id
        from   apm_packages
        where  package_key = 'acs-events';

        return apm.get_value(package_id, parameter_name);
    end get_value;

    -- This function is used internally by insert_instances
    function new_instance (
        event_id    in acs_events.event_id%TYPE,
        date_offset     in integer
    ) return acs_events.event_id%TYPE
    is
        event acs_events%ROWTYPE;
        object acs_objects%ROWTYPE;
        new_event_id acs_events.event_id%TYPE;
        new_timespan_id acs_events.timespan_id%TYPE;
    begin
         select * into event
         from   acs_events
         where  event_id = new_instance.event_id;
                
         select * into object
         from   acs_objects
         where  object_id = event_id;

         new_timespan_id := timespan.copy(event.timespan_id, date_offset);

         new_event_id := new(
            name          => event.name,
            description   => event.description,
            html_p        => event.html_p,
            status_summary => event.status_summary,
            timespan_id   => new_timespan_id,
            activity_id   => event.activity_id,
            recurrence_id => event.recurrence_id,
            creation_user => object.creation_user,
            creation_ip   => object.creation_ip,
            context_id    => object.context_id
        );

        return new_event_id;
    end new_instance;

    procedure insert_instances (
        event_id        in acs_events.event_id%TYPE, 
        cutoff_date             in date default null
    )
    is
        event            acs_events%ROWTYPE;
        recurrence       recurrences%ROWTYPE;
        new_event_id     acs_events.event_id%TYPE;
        interval_name    recurrence_interval_types.interval_name%TYPE;
        n_intervals      recurrence.every_nth_interval%TYPE;
        days_of_week     recurrence.days_of_week%TYPE;
        last_date_done   date;
        stop_date        date;
        start_date       date;
        event_date       date;
        diff             integer;
        current_date     date;
        v_last_day       date;
        week_date        date;
        instance_count   integer;
        days_length      integer;
        days_index       integer;
        day_num          integer;
    begin
        select * into event
        from   acs_events
        where  event_id = insert_instances.event_id;
        
        select * into recurrence
        from   recurrences
        where  recurrence_id = event.recurrence_id;
        
        -- Set cutoff date
        -- EventFutureLimit is in years.
        if cutoff_date is null then
           stop_date := add_months(sysdate, 12 * get_value('EventFutureLimit'));
        else
           stop_date := cutoff_date;
        end if;
        
        -- Events only populated until max(cutoff_date, recur_until)
        -- If recur_until null, then defaults to cutoff_date
        if recurrence.recur_until < stop_date then
           stop_date := recurrence.recur_until;
        end if;
        
        -- Figure out the date to start from
        select trunc(min(start_date))
        into   event_date
        from   acs_events_dates
        where  event_id = insert_instances.event_id;
        
        if recurrence.db_populated_until is null then
           start_date := event_date;
        else
           start_date := recurrence.db_populated_until;
        end if;
        
        current_date   := start_date;
        last_date_done := start_date;
        n_intervals    := recurrence.every_nth_interval;
        
        -- Case off of the interval_name to make code easier to read
        select interval_name into interval_name
        from   recurrences r, 
               recurrence_interval_types t
        where  recurrence_id   = recurrence.recurrence_id
        and    r.interval_type = t.interval_type;
        
        -- Week has to be handled specially.
        -- Start with the beginning of the week containing the start date.

        if interval_name = 'week' then
            current_date := NEXT_DAY(current_date - 7, 'SUNDAY');
            days_of_week := recurrence.days_of_week;
            days_length  := LENGTH(days_of_week);
        end if;
        
        -- Check count to prevent runaway in case of error
        instance_count := 0;
        while instance_count < 10000 and (trunc(last_date_done) <= trunc(stop_date))
        loop
            instance_count := instance_count + 1;
        
            -- Calculate next date based on interval type
            if interval_name = 'day' then
                current_date := current_date + n_intervals;
            elsif interval_name = 'month_by_date' then
                current_date := ADD_MONTHS(current_date, n_intervals);
            elsif interval_name = 'month_by_day' then
             -- Find last day of month before correct month
                v_last_day := ADD_MONTHS(LAST_DAY(current_date), n_intervals - 1);
                -- Find correct week and go to correct day of week
                current_date := NEXT_DAY(v_last_day + (7 * (to_char(current_date, 'W') - 1)), 
                                         to_char(current_date, 'DAY'));
            elsif interval_name = 'last_of_month' then
                -- Find last day of correct month
                v_last_day := LAST_DAY(ADD_MONTHS(current_date, n_intervals));
                -- Back up one week and find correct day of week
                current_date := NEXT_DAY(v_last_day - 7, to_char(current_date, 'DAY'));
            elsif interval_name = 'year' then
                current_date := ADD_MONTHS(current_date, 12 * n_intervals);
                -- Deal with custom function
            elsif interval_name = 'custom' then
                execute immediate 'current_date := ' || 
                              recurrence.custom_func || '(' || current_date || ', ' || n_intervals || ');';
            end if;
        
            -- Check to make sure we're not going past Trunc because dates aren't integral
            exit when trunc(current_date) > trunc(stop_date);
        
            -- Have to handle week specially
            if interval_name = 'week' then
                -- loop over days_of_week extracting each day number
                -- add day number and insert
                days_index := 1;
                week_date  := current_date;
                while days_index <= days_length loop
                    day_num   := SUBSTR(days_of_week, days_index, 1);
                    week_date := current_date + day_num;
                    if trunc(week_date) > trunc(start_date) and trunc(week_date) <= trunc(stop_date) then
                         -- This is where we add the event
                         new_event_id := new_instance(
                              event_id, 
                              trunc(week_date) - trunc(event_date)
                         );
                         last_date_done := week_date;
                     elsif trunc(week_date) > trunc(stop_date) then
                         -- Gone too far
                         exit;
                     end if;
                     days_index := days_index + 2;
                 end loop;

                 -- Now move to next week with repeats.
                current_date := current_date + 7 * n_intervals;
            else
                -- All other interval types
                -- This is where we add the event
                new_event_id := new_instance(
                    event_id, 
                    trunc(current_date) - trunc(event_date)
                );
                last_date_done := current_date;
            end if;
        end loop;
        
        update recurrences
        set    db_populated_until = last_date_done
        where  recurrence_id      = recurrence.recurrence_id;

    end insert_instances;


    procedure shift (
        event_id        in acs_events.event_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
    )
    is
    begin
        update acs_events_dates
        set    start_date = start_date + start_offset,
               end_date   = end_date + end_offset
        where  event_id   = shift.event_id;
    end shift;

    procedure shift_all (
        event_id        in acs_events.event_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
    )
    is
    begin
        update acs_events_dates
        set    start_date    = start_date + start_offset,
               end_date      = end_date + end_offset
        where recurrence_id  = (select recurrence_id
                                from   acs_events
                                where  event_id = shift_all.event_id);
    end shift_all;

    procedure shift_all (
        recurrence_id   in recurrences.recurrence_id%TYPE default null,
        start_offset    in number default 0,
        end_offset      in number default 0
    )
    is
    begin
        update acs_events_dates
        set    start_date   = start_date + start_offset,
               end_date     = end_date + end_offset
        where recurrence_id = shift_all.recurrence_id;
    end shift_all;

end acs_event; 
/ 
show errors

create or replace package body acs_activity
as
    function new ( 
         activity_id         in acs_activities.activity_id%TYPE   default null, 
         name                in acs_activities.name%TYPE,
         description         in acs_activities.description%TYPE   default null,
         html_p              in acs_activities.html_p%TYPE        default 'f',
         status_summary      in acs_activities.status_summary%TYPE  default null,
         object_type         in acs_object_types.object_type%TYPE default 'acs_activity', 
         creation_date       in acs_objects.creation_date%TYPE    default sysdate, 
         creation_user       in acs_objects.creation_user%TYPE    default null, 
         creation_ip         in acs_objects.creation_ip%TYPE      default null, 
         context_id          in acs_objects.context_id%TYPE       default null 
    ) return acs_activities.activity_id%TYPE
    is
        new_activity_id acs_activities.activity_id%TYPE;
    begin
        new_activity_id := acs_object.new(
            object_id     => activity_id,
            object_type   => object_type,
            creation_date => creation_date,
            creation_user => creation_user,
            creation_ip   => creation_ip,
            context_id    => context_id
        );

        insert into acs_activities
            (activity_id, name, description, html_p, status_summary)
        values
            (new_activity_id, name, description, html_p, status_summary);

        return new_activity_id;
    end new;


    function name (
        -- name method
        -- @author gjin@arsdigita.com
        -- @param activity_id
        --
        activity_id          in acs_activities.activity_id%TYPE
        
    ) return acs_activities.name%TYPE
        
    is
        new_activity_name    acs_activities.name%TYPE;

    begin
        select  name
        into    new_activity_name
        from    acs_activities
        where   activity_id = name.activity_id;

        return  new_activity_name;
    end;

         
    procedure del ( 
         activity_id in acs_activities.activity_id%TYPE 
    )
    is
    begin
         -- Cascade will cause delete from acs_activities 
         -- and acs_activity_object_map

         acs_object.del(activity_id); 
    end del;

    -- NOTE: can't use update

    procedure edit (
         activity_id     in acs_activities.activity_id%TYPE, 
         name            in acs_activities.name%TYPE default null,
         description     in acs_activities.description%TYPE default null,
         html_p          in acs_activities.html_p%TYPE default null,
         status_summary  in acs_activities.status_summary%TYPE default null
    )
    is
    begin
        update acs_activities
        set    name        = nvl(edit.name, name),
               description = nvl(edit.description, description),
               html_p      = nvl(edit.html_p, html_p),
               status_summary = nvl(edit.status_summary, status_summary)
        where activity_id  = edit.activity_id;
    end edit;

    procedure object_map (
        activity_id in acs_activities.activity_id%TYPE, 
        object_id   in acs_objects.object_id%TYPE
    )
    is
    begin
        insert into acs_activity_object_map
            (activity_id, object_id)
        values
            (activity_id, object_id);
    end object_map;

    procedure object_unmap (
        activity_id in acs_activities.activity_id%TYPE, 
         object_id  in acs_objects.object_id%TYPE
    )
    is
    begin
        delete from acs_activity_object_map
        where  activity_id = object_unmap.activity_id
        and    object_id   = object_unmap.object_id;
    end object_unmap;

end acs_activity;
/
show errors

create or replace package body recurrence
as
    function new (
         interval_type          in recurrence_interval_types.interval_name%TYPE,
         every_nth_interval     in recurrences.every_nth_interval%TYPE,
         days_of_week           in recurrences.days_of_week%TYPE default null,
         recur_until            in recurrences.recur_until%TYPE default null,
         custom_func            in recurrences.custom_func%TYPE default null
    ) return recurrences.recurrence_id%TYPE
    is
        recurrence_id recurrences.recurrence_id%TYPE;
        interval_type_id recurrence_interval_types.interval_type%TYPE;
    begin
        select recurrence_seq.nextval into recurrence_id from dual;
        
        select interval_type
        into   interval_type_id 
        from   recurrence_interval_types
        where  interval_name = new.interval_type;
        
        insert into recurrences
            (recurrence_id, 
             interval_type, 
             every_nth_interval, 
             days_of_week,
             recur_until, 
             custom_func)
        values
            (recurrence_id, 
             interval_type_id, 
             every_nth_interval, 
             days_of_week,
             recur_until, 
             custom_func);
         
        return recurrence_id;
    end new;

    -- Note: this will fail if there are any events_with this recurrence
    procedure del (
         recurrence_id in recurrences.recurrence_id%TYPE
    )
    is
    begin
        delete from recurrences
        where  recurrence_id = recurrence.del.recurrence_id;
    end del;

end recurrence;
/
show errors

create or replace package body time_interval
as
    function new (
        start_date  in time_intervals.start_date%TYPE default null,
        end_date    in time_intervals.end_date%TYPE default null
    ) return time_intervals.interval_id%TYPE
    is
        interval_id time_intervals.interval_id%TYPE;
    begin
        select timespan_seq.nextval into interval_id from dual;

        insert into time_intervals 
            (interval_id, start_date, end_date)
        values
            (interval_id, start_date, end_date);
                
        return interval_id;
    end new;

    procedure del (
        interval_id in time_intervals.interval_id%TYPE
    )
    is
    begin
        delete time_intervals
        where  interval_id = time_interval.del.interval_id;
    end del;

    procedure edit (
        interval_id     in time_intervals.interval_id%TYPE,
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    )
    is
    begin
        -- Null for start_date or end_date means don't change.
        if start_date is not null and end_date is not null then
            update time_intervals
            set    start_date  = edit.start_date,
                   end_date    = edit.end_date
            where  interval_id = edit.interval_id;
        elsif start_date is not null then
            update time_intervals
            set    start_date  = edit.start_date
            where  interval_id = edit.interval_id;
        elsif end_date is not null then
            update time_intervals
            set end_date       = edit.end_date
            where interval_id  = edit.interval_id;
        end if;
    end edit;

    procedure shift (
        interval_id      in time_intervals.interval_id%TYPE,
        start_offset     in number default 0,
        end_offset       in number default 0
    )
    is
    begin
        update time_intervals
        set    start_date = start_date + start_offset,
               end_date   = end_date + end_offset
        where  interval_id = shift.interval_id;
    end shift;

    function overlaps_p (
        interval_1_id   in time_intervals.interval_id%TYPE,
        interval_2_id   in time_intervals.interval_id%TYPE
    ) return char
    is
        start_1 date;
        start_2 date;
        end_1 date;
        end_2 date;
    begin
        -- Pull out the start and end dates and call the main overlaps_p.
        select start_date, 
               end_date
        into   start_1, 
               end_1
        from   time_intervals
        where  interval_id = interval_1_id;

        select start_date, 
               end_date
        into   start_2, 
               end_2
        from   time_intervals
        where  interval_id = interval_2_id;

        return overlaps_p(start_1, end_1, start_2, end_2);
    end overlaps_p;

    function overlaps_p (
        interval_id     in time_intervals.interval_id%TYPE,
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return char
    is
        interval_start time_intervals.start_date%TYPE;
        interval_end   time_intervals.end_date%TYPE;
    begin
        -- Pull out the start and end date and call the main overlaps_p.
        select start_date, 
               end_date
        into   interval_start, 
               interval_end
        from   time_intervals
        where  interval_id = overlaps_p.interval_id;

        return overlaps_p(interval_start, interval_end, start_date, end_date);
    end overlaps_p;

    function overlaps_p (
        start_1 in time_intervals.start_date%TYPE,
        end_1   in time_intervals.end_date%TYPE,
        start_2 in time_intervals.start_date%TYPE,
        end_2   in time_intervals.end_date%TYPE
    ) return char
    is
    begin
        if start_1 is null then
            -- No overlap if 2nd interval starts after 1st ends
            if end_1 < start_2 then
                return 'f';
            else
                return 't';
            end if;
        elsif start_2 is null then
            -- No overlap if 2nd interval ends before 1st starts
            if end_2 < start_1 then
                return 'f';
            else
                return 't';
            end if;
        -- Okay, both start dates are not null
            elsif start_1 <= start_2 then
            -- 1st starts before 2nd
                if end_1 < start_2 then
                    -- No overlap if 1st ends before 2nd starts
                    return 'f';
                else
                    -- No overlap or at least one null
                    return 't';
                end if;
            else
                -- 1st starts after 2nd
                if end_2 < start_1 then
                    -- No overlap if 2nd ends before 1st starts
                    return 'f';
                else
                    -- No overlap or at least one null
                    return 't';
                end if;
            end if;
    end overlaps_p;

    function eq (
        -- Checks if two intervals are equivalent
        interval_1_id   in time_intervals.interval_id%TYPE,
        interval_2_id   in time_intervals.interval_id%TYPE
    ) return boolean
    is
        interval_1 time_intervals%ROWTYPE;
        interval_2 time_intervals%ROWTYPE;
    begin
        select * into interval_1
        from   time_intervals
        where  interval_id = interval_1_id;

        select * into interval_2
        from   time_intervals
        where  interval_id = interval_2_id;

        if interval_1.start_date = interval_2.start_date and 
           interval_1.end_date = interval_2.end_date then
            return true;
        else
            return false;
        end if;
    end eq;

    function copy (
        interval_id     in time_intervals.interval_id%TYPE,
        offset          in integer default 0
    ) return time_intervals.interval_id%TYPE
    is
        interval time_intervals%ROWTYPE;
    begin
        select * into interval
        from   time_intervals
        where  interval_id = copy.interval_id;

        return new(interval.start_date + offset, interval.end_date + offset);
    end copy;

end time_interval;
/
show errors

create or replace package body timespan
as
    function new (
        interval_id in time_intervals.interval_id%TYPE
    ) return timespans.timespan_id%TYPE
    is
        timespan_id      timespans.timespan_id%TYPE;
        new_interval_id  time_intervals.interval_id%TYPE;
    begin
        select timespan_seq.nextval into timespan_id from dual;
        
        new_interval_id := time_interval.copy(interval_id);
        
        insert into timespans
            (timespan_id, interval_id)
        values
            (timespan_id, new_interval_id);
        
        return timespan_id;
    end new;

    function new (
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return timespans.timespan_id%TYPE
    is
    begin
        return new(time_interval.new(start_date, end_date));
    end new;

    procedure del (
        timespan_id in timespans.timespan_id%TYPE
    )
    is
    begin
        -- Delete intervals, corresponding timespan entries deleted by
        -- cascading constraints
        delete from time_intervals
        where  interval_id in (select interval_id
                               from   timespans
                               where  timespan_id = timespan.del.timespan_id);
    end del;

    --
    -- Join a new timespan or time interval to an existing timespan
    --
    procedure join (
        timespan_1_id   in timespans.timespan_id%TYPE,
        timespan_2_id   in timespans.timespan_id%TYPE
    )
    is
        cursor timespan_cursor is
            select * 
            from   timespans
            where  timespan_id = timespan_2_id;
        timespan_val timespan_cursor%ROWTYPE;
    begin
        -- Loop over intervals in 2nd timespan, join with 1st.
        for timespan_val in timespan_cursor
        loop
            join_interval(timespan_1_id, timespan_val.interval_id);
        end loop;
    end join;

    -- Optional argument to copy interval
    procedure join_interval (
         timespan_id     in timespans.timespan_id%TYPE,
         interval_id     in time_intervals.interval_id%TYPE,
         copy_p          in boolean default true
    )
    is
        new_interval_id time_intervals.interval_id%TYPE;
    begin
        if copy_p then
           new_interval_id := time_interval.copy(interval_id);
        else
           new_interval_id := interval_id;
        end if;
        
        insert into timespans
            (timespan_id, interval_id)
        values
            (timespan_id, new_interval_id);
    end join_interval;

    procedure join (
        timespan_id in timespans.timespan_id%TYPE,
        start_date  in time_intervals.start_date%TYPE default null,
        end_date    in time_intervals.end_date%TYPE default null
    )
    is
    begin
        join_interval(
            timespan_id => timespan_id, 
            interval_id => time_interval.new(start_date, end_date),
            copy_p      => false
        );
    end join;

    procedure interval_delete (
        timespan_id in timespans.timespan_id%TYPE,
        interval_id in time_intervals.interval_id%TYPE
    )
    is
    begin
        delete from timespans
        where timespan_id = interval_delete.timespan_id
        and   interval_id = interval_delete.interval_id;
    end interval_delete;

    -- Information

    function exists_p (
        timespan_id in timespans.timespan_id%TYPE
    ) return char
    is
        result integer;
    begin
        -- Only need to check if any rows exist.
        select count(*)
        into   result
        from   dual 
        where  exists (select timespan_id
                       from   timespans
                       where  timespan_id = exists_p.timespan_id);
        if result = 0 then
           return 'f';
        else
           return 't';
        end if;
    end exists_p;

    function multi_interval_p (
        timespan_id in timespans.timespan_id%TYPE
    ) return char
    is
        result char;
    begin
        -- 'f' if 0 or 1 intervals, 't' otherwise
        select decode(count(timespan_id), 0, 'f', 1, 'f', 't')
        into result
        from timespans
        where timespan_id = multi_interval_p.timespan_id;
        
        return result;
    end multi_interval_p;


    function overlaps_p (
        -- Checks to see if any intervals in a timespan overlap any of the intervals
        -- in the second timespan.
        timespan_1_id   in timespans.timespan_id%TYPE,
        timespan_2_id   in timespans.timespan_id%TYPE
    ) return char
    is
        result char;
        cursor timespan_cursor is
            select * 
            from timespans
            where timespan_id = timespan_2_id;
        timespan_val timespan_cursor%ROWTYPE;
    begin
        -- Loop over 2nd timespan, checking each interval against 1st
        for timespan_val in timespan_cursor
        loop
            result := overlaps_interval_p
                (timespan_1_id,
                 timespan_val.interval_id
            );
            if result = 't' then
                return 't';
            end if;
        end loop;
        return 'f';
    end overlaps_p;

    function overlaps_interval_p (
        timespan_id in timespans.timespan_id%TYPE,
        interval_id in time_intervals.interval_id%TYPE default null
    ) return char
    is
        start_date date;
        end_date date;
    begin
        select start_date, end_date
        into   start_date, end_date
        from   time_intervals
        where  interval_id = overlaps_interval_p.interval_id;
        
        return overlaps_p(timespan_id, start_date, end_date);
    end overlaps_interval_p;

    function overlaps_p (
        timespan_id     in timespans.timespan_id%TYPE,
        start_date      in time_intervals.start_date%TYPE default null,
        end_date        in time_intervals.end_date%TYPE default null
    ) return char
    is
        result char;
        cursor timespan_cursor is
            select * 
            from timespans
            where timespan_id = overlaps_p.timespan_id;
        timespan_val timespan_cursor%ROWTYPE;
    begin
        -- Loop over each interval in timespan, checking against dates.
        for timespan_val in timespan_cursor
        loop
            result := time_interval.overlaps_p(
                timespan_val.interval_id, 
                start_date,
                end_date
            );

            if result = 't' then
                return 't';
            end if;
        end loop;
        return 'f';
    end overlaps_p;

    function copy (
        timespan_id in timespans.timespan_id%TYPE,
        offset      in integer default 0
    ) return timespans.timespan_id%TYPE
    is
        cursor timespan_cursor is
            select * 
            from timespans
            where timespan_id = copy.timespan_id;
        timespan_val     timespan_cursor%ROWTYPE;
        new_interval_id  timespans.interval_id%TYPE;
        new_timespan_id  timespans.timespan_id%TYPE;
     begin
        new_timespan_id := null;

        -- Loop over each interval in timespan, creating a new copy
        for timespan_val in timespan_cursor
        loop
            new_interval_id := time_interval.copy(timespan_val.interval_id, offset);

            if new_timespan_id is null then
                new_timespan_id := new(new_interval_id);
            else
                join_interval(new_timespan_id, new_interval_id);
            end if;
        end loop;
        return new_timespan_id;
    end copy;

end timespan;
/
show errors

