using System;
using System.Collections;
using System.Data;
//using System.Data.OleDb;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using IndianHealthService.BMXNet;
namespace IndianHealthService.ClinicalScheduling
{
///
/// Contains array of availability blocks for a scheduling resource
///
public class CGAVDocument : System.Object
{
public CGAVDocument()
{
m_AVBlocks = new CGAppointments();
m_sResourcesArray = new ArrayList();
}
#region Member Variables
public int m_nColumnCount; //todo: this should point to the view's member for column count
public int m_nTimeUnits;
public string m_sSecondary;
private string m_sDocName;
public ArrayList m_sResourcesArray;
public ScheduleType m_ScheduleType;
private DateTime m_dSelectedDate; //Holds the user's selection from the dtpicker
private DateTime m_dStartDate; //Beginning date of document data
private DateTime m_dEndDate; //Ending date of document data
public CGAppointments m_AVBlocks;
private CGDocumentManager m_DocManager;
private int m_nDocID; //Resource IEN in ^BSDXRES
#endregion
#region Properties
///
/// Resource IEN in ^BSDXRES
///
public int ResourceID
{
get
{
return m_nDocID;
}
set
{
m_nDocID = value;
}
}
///
/// The list of Resource names
///
public ArrayList Resources
{
get
{
return this.m_sResourcesArray;
}
set
{
this.m_sResourcesArray = value;
}
}
public CGDocumentManager DocManager
{
get
{
return m_DocManager;
}
set
{
m_DocManager = value;
}
}
///
/// Contains the hashtable of Availability Blocks
///
public CGAppointments AVBlocks
{
get
{
return m_AVBlocks;
}
set
{
m_AVBlocks = value;
}
}
///
/// Holds the date selected by the user in CGView.dateTimePicker1
///
public DateTime SelectedDate
{
get
{
return this.m_dSelectedDate;
}
set
{
this.m_dSelectedDate = value;
bool bRet = false;
if (m_ScheduleType == ScheduleType.Resource)
{
bRet = this.WeekNeedsRefresh(1, m_dSelectedDate, out this.m_dStartDate, out this.m_dEndDate);
}
else
{
this.m_dStartDate = m_dSelectedDate;
this.m_dEndDate = m_dSelectedDate;
this.m_dEndDate = this.m_dEndDate.AddHours(23);
this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
}
bRet = this.RefreshDaysSchedule();
}
}
///
/// Contains the beginning date of the appointment document
///
public DateTime StartDate
{
get
{
return this.m_dStartDate;
}
}
public string DocName
{
get
{
return this.m_sDocName;
}
set
{
this.m_sDocName = value;
}
}
#endregion
#region Methods
public void ChangeAppointmentTime(CGAppointment pAppt, DateTime dNewStart, DateTime dNewEnd, string sResource)
{
try
{
DateTime dOldStart = pAppt.StartTime;
DateTime dOldEnd = pAppt.EndTime;
if ((dOldStart == dNewStart) && (dOldEnd == dNewEnd))
{ //no change in time
return;
}
foreach (CGAppointment a in m_AVBlocks.AppointmentTable.Values)
{
DateTime sStart2 = a.StartTime;
DateTime sEnd2 = a.EndTime;
if ((a.AppointmentKey != pAppt.AppointmentKey) && (CalendarGrid.TimesOverlap(dNewStart, dNewEnd, a.StartTime, a.EndTime)))
{
MessageBox.Show("Access blocks may not overlap.","Clinical Scheduling", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
}
this.DeleteAvailability(pAppt.AppointmentKey);
pAppt.StartTime = dNewStart;
pAppt.EndTime = dNewEnd;
pAppt.Resource = sResource;
this.CreateAppointment(pAppt);
}
catch(Exception e)
{
MessageBox.Show("CGDocument::ChangeAppointmentTime failed: " + e.Message);
return;
}
}
public void DeleteAvailability(int nApptID)
{
if (this.m_AVBlocks.AppointmentTable.ContainsKey(nApptID))
{
string sSql = "BSDX CANCEL AVAILABILITY^" + nApptID.ToString();
DataTable dtAppt =m_DocManager.RPMSDataTable(sSql, "DeleteAvailability");
int nErrorID;
Debug.Assert(dtAppt.Rows.Count == 1);
DataRow r = dtAppt.Rows[0];
nErrorID = Convert.ToInt32(r["ERRORID"]);
this.m_AVBlocks.RemoveAppointment(nApptID);
UpdateAllViews();
}
}
///
/// Called by LoadTemplate to create Access Block
/// Returns the IEN of the availability block in the RPMS BSDX AVAILABILITY file.
///
public int CreateAppointmentAuto(CGAppointment rApptInfo)
{
try
{
string sStart;
string sEnd;
string sResource;
string sNote;
string sTypeID;
string sSlots;
//sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
//sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
// i18n support
sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
sNote = rApptInfo.Note;
sResource = rApptInfo.Resource;
sTypeID = rApptInfo.AccessTypeID.ToString();
sSlots = rApptInfo.Slots.ToString();
CGAppointment aCopy = new CGAppointment();
aCopy.CreateAppointment(rApptInfo.StartTime, rApptInfo.EndTime, sNote, 0, sResource);
aCopy.AccessTypeID = rApptInfo.AccessTypeID;
aCopy.Slots = rApptInfo.Slots;
aCopy.IsAccessBlock = true;
string sSql = "BSDX ADD NEW AVAILABILITY^" + sStart + "^" + sEnd + "^" + sTypeID + "^" + sResource + "^" + sSlots + "^" + sNote;
DataTable dtAppt = m_DocManager.RPMSDataTable(sSql, "NewAvailability");
int nApptID;
int nErrorID;
Debug.Assert(dtAppt.Rows.Count == 1);
DataRow r = dtAppt.Rows[0];
nApptID =Convert.ToInt32(r["AVAILABILITYID"]);
nErrorID = Convert.ToInt32(r["ERRORID"]);
if (nErrorID != -1)
{
throw new Exception("VistA Error");
}
Debug.Write("CreateAvailabilityAuto -- new AV block created\n");
//UpdateAllViews();
aCopy.AppointmentKey = nApptID;
return nApptID;
}
catch (Exception ex)
{
Debug.Write("CGAVDocument.CreateAppointmentAuto Failed: " + ex.Message);
return -1;
}
}
public int CreateAppointment(CGAppointment rApptInfo)
{
try
{
string sStart;
string sEnd;
string sResource;
string sNote;
string sTypeID;
string sSlots;
//sStart = rApptInfo.StartTime.ToString("M-d-yyyy@HH:mm");
//sEnd = rApptInfo.EndTime.ToString("M-d-yyyy@HH:mm");
// i18n support
sStart = FMDateTime.Create(rApptInfo.StartTime).FMDateString;
sEnd = FMDateTime.Create(rApptInfo.EndTime).FMDateString;
sNote = rApptInfo.Note;
sResource = rApptInfo.Resource;
sTypeID = rApptInfo.AccessTypeID.ToString();
sSlots = rApptInfo.Slots.ToString();
CGAppointment aCopy = new CGAppointment();
aCopy.CreateAppointment(rApptInfo.StartTime, rApptInfo.EndTime, sNote, 0, sResource);
aCopy.AccessTypeID = rApptInfo.AccessTypeID;
aCopy.Slots = rApptInfo.Slots;
aCopy.IsAccessBlock = true;
aCopy.AccessTypeName = this.AccessNameFromID(aCopy.AccessTypeID);
foreach (CGAppointment a in this.m_AVBlocks.AppointmentTable.Values)
{
DateTime sStart2 = a.StartTime;
DateTime sEnd2 = a.EndTime;
if (CalendarGrid.TimesOverlap(aCopy.StartTime, aCopy.EndTime, a.StartTime, a.EndTime))
{
// throw new Exception("Access blocks may not overlap.");
MessageBox.Show("Access blocks may not overlap.","Clinical Scheduling", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return -1;
}
}
string sSql = "BSDX ADD NEW AVAILABILITY^" + sStart + "^" + sEnd + "^" + sTypeID + "^" + sResource + "^" + sSlots + "^" + sNote;
DataTable dtAppt =m_DocManager.RPMSDataTable(sSql, "NewAvailability");
int nApptID;
int nErrorID;
Debug.Assert(dtAppt.Rows.Count == 1);
DataRow r = dtAppt.Rows[0];
nApptID =Convert.ToInt32(r["AVAILABILITYID"]);
nErrorID = Convert.ToInt32(r["ERRORID"]);
if (nErrorID != -1)
{
throw new Exception("VistA Error");
}
Debug.Write("CreateAvailability -- new AV block created\n");
aCopy.AppointmentKey = nApptID;
this.m_AVBlocks.AddAppointment(aCopy);
UpdateAllViews();
return nApptID;
}
catch (Exception ex)
{
Debug.Write("CGAVDocument.CreateAppointment Failed: " + ex.Message);
return -1;
}
}
private int GetTotalMinutes(DateTime dDate)
{
return ((dDate.Hour * 60) + dDate.Minute);
}
private string AccessNameFromID(int nAccessTypeID)
{
DataView dvTypes = new DataView(this.DocManager.GlobalDataSet.Tables["AccessTypes"]);
dvTypes.Sort = "BMXIEN ASC";
int nRow = dvTypes.Find(nAccessTypeID);
DataRowView drv = dvTypes[nRow];
string sAccessTypeName = drv["ACCESS_TYPE_NAME"].ToString();
return sAccessTypeName;
}
///
/// Update availability block schedule based on info in RPMS
///
public bool RefreshDaysSchedule()
{
DateTime dStart;
DateTime dEnd;
int nKeyID;
string sNote;
string sResource;
string sAccessType;
string sSlots;
CGAppointment pAppointment;
CGDocumentManager pApp = CGDocumentManager.Current;
DataTable rAppointmentSchedule;
this.m_AVBlocks.ClearAllAppointments();
ArrayList apptTypeIDs = new ArrayList();
rAppointmentSchedule = CGSchedLib.CreateAvailabilitySchedule(m_DocManager, m_sResourcesArray, this.m_dStartDate, this.m_dEndDate, apptTypeIDs,/* */ this.m_ScheduleType, "0");
foreach (DataRow r in rAppointmentSchedule.Rows)
{
nKeyID = Convert.ToInt32(r["AVAILABILITYID"].ToString());
if (nKeyID > 0)
{
dStart = (DateTime) r["START_TIME"];
dEnd = (DateTime) r["END_TIME"];
sNote = r["NOTE"].ToString();
sResource = r["RESOURCE"].ToString();
sAccessType = r["ACCESS_TYPE"].ToString();
sSlots = r["SLOTS"].ToString();
pAppointment = new CGAppointment();
pAppointment.CreateAppointment(dStart, dEnd, sNote, nKeyID, sResource);
pAppointment.AccessTypeID = Convert.ToInt16(sAccessType);
pAppointment.Slots = Convert.ToInt16(sSlots);
pAppointment.IsAccessBlock = true;
pAppointment.AccessTypeName = this.AccessNameFromID(pAppointment.AccessTypeID);
this.m_AVBlocks.AddAppointment(pAppointment);
}
}
return true;
}
///
/// Given a selected date,
/// Calculates StartDay and End Day and returns them in output params.
/// nWeeks == number of Weeks to display
/// nColumnCount is number of days displayed per week.
/// If 5 columns, begin on Second Day of Week
/// If 7 Columns, begin on First Day of Week
/// (this is a change from the hardcoded behavior for US-based calendars)
///
/// Returns TRUE if the document's data needs refreshing based on
/// this newly selected date.
///
/// TODO: This is a duplicate of the method in CGDocument. We need to put them together.
public bool WeekNeedsRefresh(int nWeeks, DateTime SelectedDate,
out DateTime WeekStartDay, out DateTime WeekEndDay)
{
DateTime OldStartDay = m_dStartDate;
DateTime OldEndDay = m_dEndDate;
// Week start based on thread locale
int nStartWeekDay = (int)System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
// Current Day
int nWeekDay = (int)SelectedDate.DayOfWeek; //0 == Sunday
// this offset gets approrpriate day based on locale.
int nOff = (nStartWeekDay + 1) % 7;
TimeSpan ts = new TimeSpan(nWeekDay - nOff, 0, 0, 0); //d,h,m,s
// if ts is negative, we will jump to the next week in the logic.
// to show the correct week, add 7. Confusing, I know.
if (ts < new TimeSpan()) ts = ts + new TimeSpan(7, 0, 0, 0);
if (m_nColumnCount == 1) // if one column start and end on the same day.
{
ts = new TimeSpan(0, 23, 59, 59);
WeekStartDay = SelectedDate;
WeekEndDay = WeekStartDay + ts;
}
else if (m_nColumnCount == 5 || m_nColumnCount == 0) // if 5 column start (or default) at the 2nd day of this week and end in 4:23:59:59 days.
{
// if picked day is start of week (Sunday in US), start in the next day since that's the first day of work week
// else, just substract the calculated time span to get to the start of week (first work day)
WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate + new TimeSpan(1, 0, 0, 0) : SelectedDate - ts;
// End day calculation
int nEnd = 3;
ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
WeekEndDay = WeekStartDay + ts;
}
else // if 7 column start at the 1st day of this week and end in 6:23:59:59 days.
{
// if picked day is start of week, use that. Otherwise, go to the fist work day and substract one to get to start of week.
WeekStartDay = (nWeekDay == nStartWeekDay) ? SelectedDate : SelectedDate - ts - new TimeSpan(1, 0, 0, 0);
// End day calculation
int nEnd = 1;
ts = new TimeSpan((7 * nWeeks) - nEnd, 23, 59, 59);
WeekEndDay = WeekStartDay + ts;
}
bool bRet = ((WeekStartDay.Date != OldStartDay.Date) || (WeekEndDay.Date != OldEndDay.Date));
return bRet;
}
public void OnOpenDocument()
{
//Create new Document
// DateTime dStart;
// DateTime dEnd;
Debug.Assert(m_sResourcesArray.Count > 0);
m_sSecondary = "";
m_ScheduleType = (m_sResourcesArray.Count == 1) ? ScheduleType.Resource: ScheduleType.Clinic;
bool bRet = false;
//Set initial From and To dates based on current day
// DateTime dDate = new DateTime(2001,12,05); //test line
DateTime dDate = DateTime.Today;
if (m_ScheduleType == ScheduleType.Resource)
{
bRet = this.WeekNeedsRefresh(1,dDate, out this.m_dStartDate, out this.m_dEndDate);
}
else
{
this.m_dStartDate = dDate;
this.m_dEndDate = dDate;
this.m_dEndDate = this.m_dEndDate.AddHours(23);
this.m_dEndDate = this.m_dEndDate.AddMinutes(59);
this.m_dEndDate = this.m_dEndDate.AddSeconds(59);
}
bRet = this.RefreshDaysSchedule();
CGAVView view = null;
//If this document already has a view, the use it
Hashtable h = CGDocumentManager.Current.AvailabilityViews;
CGAVDocument d;
bool bReuseView = false;
foreach (CGAVView v in h.Keys)
{
d = (CGAVDocument) h[v];
if (d == this)
{
view = v;
bReuseView = true;
break;
}
}
//Otherwise, create new View
if (bReuseView == false)
{
view = new CGAVView();
view.DocManager = this.DocManager;
view.StartDate = m_dStartDate;
//Assign the document to the view
view.Document = this;
//Link the calendargrid's appointments table to the document's table
view.AVBlocks = this.AVBlocks;
view.Text = "Edit Availability - " + this.DocName;
view.Show();
}
this.UpdateAllViews();
}
public void AddResource(string sResource)
{
//TODO: Test that resource is not currently in list, that it IS a resource, etc
this.m_sResourcesArray.Add(sResource);
this.UpdateAllViews();
}
///
/// Calls each AVview associated with this AVdocument and tells it to update itself
///
public void UpdateAllViews()
{
//iterate through all views and call update.
Hashtable h = CGDocumentManager.Current.AvailabilityViews;
CGAVDocument d;
foreach (CGAVView v in h.Keys)
{
d = (CGAVDocument) h[v];
if (d == this)
{
v.UpdateArrays();
}
}
}
#endregion
}//End class
}