1/*
2 * Copyright (C) 2012 Cutehacks AS. All rights reserved.
3 * info@cutehacks.com
4 * http://cutehacks.com
5 *
6 * This file is part of Fly.
7 *
8 * Fly is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Fly is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Fly. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "flightsmodel.h"
23
24#include "settings.h"
25#include "airportnames.h"
26#include "flightstatuses.h"
27#include "node.h"
28
29FlightsModel::FlightsModel(Settings *settings,
30 AirportNames *airports,
31 FlightStatuses *statuses,
32 FlightTimes *times,
33 QObject *parent)
34 : QAbstractListModel(parent),
35 m_settings(settings),
36 m_airports(airports),
37 m_statuses(statuses),
38 m_times(times)
39{
40 QHash<int,QByteArray> names;
41 names.insert(0, "uniqueID");
42 names.insert(1, "airline");
43 names.insert(2, "flightID");
44 names.insert(3, "domesticOrInternational");
45 names.insert(4, "scheduleTime");
46 names.insert(5, "arrivalOrDeparture");
47 names.insert(6, "airportCode");
48 names.insert(7, "airportName");
49 names.insert(8, "viaAirportCodes");
50 names.insert(9, "viaAirportNames");
51 names.insert(10, "routeAirportNames");
52 names.insert(11, "checkinArea");
53 names.insert(12, "gate");
54 names.insert(13, "statusCode");
55 names.insert(14, "statusText");
56 names.insert(15, "statusTime");
57 names.insert(16, "beltNumber");
58 names.insert(17, "roundtrip");
59 names.insert(18, "scheduleText");
60 setRoleNames(names);
61
62 m_times->setAirportCode(m_settings->airportCode());
63 QObject::connect(m_times, SIGNAL(dataUpdated(Node*)), this, SLOT(updateContents(Node*)));
64 QObject::connect(m_times, SIGNAL(airportCodeChanged()), this, SIGNAL(airportCodeChanged()));
65 QObject::connect(m_settings, SIGNAL(airportCodeChanged()), this, SLOT(reloadContents()));
66}
67
68FlightsModel::~FlightsModel()
69{
70}
71
72int FlightsModel::rowCount(const QModelIndex &parent) const
73{
74 return parent.isValid() ? 0 : m_flights.count();
75}
76
77bool FlightsModel::isLoading() const
78{
79 return m_flights.isEmpty();// && m_times && m_times->isWaiting();
80}
81
82QString FlightsModel::airportCode() const
83{
84 return m_times ? m_times->airportCode() : QString();
85}
86
87QVariant FlightsModel::data(const QModelIndex &index, int role) const
88{
89 if (index.isValid()) {
90 switch (role) {
91 case 0: return m_flights.value(index.row()).m_uniqueID;
92 case 1: return m_flights.value(index.row()).m_airline;
93 case 2: return m_flights.value(index.row()).m_flightID;
94 case 3: return m_flights.value(index.row()).m_domesticOrInternational;
95 case 4: return m_flights.value(index.row()).m_scheduleTime;
96 case 5: return m_flights.value(index.row()).m_arrivalOrDeparture;
97 case 6: return m_flights.value(index.row()).m_airportCode;
98 case 7: return m_flights.value(index.row()).m_airportName;
99 case 8: return m_flights.value(index.row()).m_viaAirportCodes;
100 case 9: return m_flights.value(index.row()).m_viaAirportNames;
101 case 10: return m_flights.value(index.row()).m_routeAirportNames;
102 case 11: return m_flights.value(index.row()).m_checkinArea;
103 case 12: return m_flights.value(index.row()).m_gate;
104 case 13: return m_flights.value(index.row()).m_statusCode;
105 case 14: return m_flights.value(index.row()).m_statusText;
106 case 15: return m_flights.value(index.row()).m_statusTime;
107 case 16: return m_flights.value(index.row()).m_beltNumber;
108 case 17: return m_flights.value(index.row()).m_roundtrip;
109 case 18: return m_flights.value(index.row()).m_scheduleText;
110 default: break;
111 }
112 }
113 return QVariant();
114}
115
116void FlightsModel::reloadContents()
117{
118 if (m_times) {
119 m_times->setAirportCode(m_settings->airportCode());
120 m_times->start();
121 }
122 beginResetModel();
123 m_flights.clear();
124 endResetModel();
125 emit loadingChanged();
126}
127
128void FlightsModel::updateContents(Node *root)
129{
130 beginResetModel();
131 m_flights.clear();
132
133 QStack<Node*> pending;
134 pending.push(root);
135 while (!pending.isEmpty()) {
136 Node *node = pending.pop();
137 for (int i = 0; i < node->childCount(); ++i) {
138 Node *child = node->child(i);
139 if (child->type() == "flight") {
140 appendFlightData(child);
141 } else {
142 pending.push(child);
143 }
144 }
145 }
146
147 endResetModel();
148 emit loadingChanged();
149}
150
151void FlightsModel::appendFlightData(Node *flight)
152{
153 const QString airportCode = m_times->airportCode();
154 FlightData data;
155 if (flight && flight->type() == "flight") {
156 data.m_uniqueID = flight->attribute("uniqueID").toLong();
157 if (Node *airline = flight->firstChildOfType("airline"))
158 data.m_airline = airline->text();
159 if (Node *flight_id = flight->firstChildOfType("flight_id"))
160 data.m_flightID = flight_id->text();
161 if (Node *dom_int = flight->firstChildOfType("dom_int"))
162 data.m_domesticOrInternational = dom_int->text(); // "D" = domestic, "I" = international
163 if (Node *schedule_time = flight->firstChildOfType("schedule_time")) {
164 QDateTime scheduleTime = QDateTime::fromString(schedule_time->text(), Qt::ISODate);
165 scheduleTime.setTimeSpec(Qt::UTC);
166 data.m_scheduleTime = scheduleTime.toLocalTime();
167 data.m_scheduleText = data.m_scheduleTime.toString("hhmm");
168 }
169 if (Node *arr_dep = flight->firstChildOfType("arr_dep"))
170 data.m_arrivalOrDeparture = arr_dep->text(); // "D" = departure, "A" = arrival
171 if (Node *airport = flight->firstChildOfType("airport")) {
172 data.m_airportCode = airport->text();
173 data.m_airportName = m_airports ? m_airports->airportName(data.m_airportCode) : QString();
174 data.m_roundtrip = (data.m_airportCode == airportCode); // if destination and origin are the same
175 }
176 if (Node *via_airport = flight->firstChildOfType("via_airport")) {
177 data.m_viaAirportCodes = via_airport->text().split(',');
178 foreach(QString code, data.m_viaAirportCodes)
179 data.m_viaAirportNames.append(m_airports->airportName(code));
180 if (!data.m_roundtrip && data.m_arrivalOrDeparture == "D")
181 data.m_viaAirportNames.append(data.m_airportName);
182 data.m_routeAirportNames = data.m_viaAirportNames.join(", ");
183 }
184 if (Node *check_in = flight->firstChildOfType("check_in"))
185 data.m_checkinArea = check_in->text(); // "A" "B" "C" "D" checking area
186 if (Node *gate = flight->firstChildOfType("gate"))
187 data.m_gate = gate->text();
188 if (Node *status = flight->firstChildOfType("status")) {
189 data.m_statusCode = status->attribute("code");
190 data.m_statusText = m_statuses ? m_statuses->statusTextNorwegian(data.m_statusCode) : QString(); // ### support norwegian
191 QDateTime statusTime = QDateTime::fromString(status->attribute("time"), Qt::ISODate);
192 statusTime.setTimeSpec(Qt::UTC);
193 data.m_statusText += statusTime.toLocalTime().toString(" hh:mm");
194 }
195 if (Node *belt_number = flight->firstChildOfType("belt_number"))
196 data.m_beltNumber = belt_number->text();
197 }
198 m_flights.append(data);
199}
200