aboutsummaryrefslogtreecommitdiffstats
path: root/MainApp.cpp
blob: a5a3865cde89d2e1a3a5f7b9cddace137555e58c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/iotbzh/xds-agent/lib/xaapiv1"
	"github.com/urfave/cli"
)

func initCmdExec(cmdDef *[]cli.Command) {
	*cmdDef = append(*cmdDef, cli.Command{
		Name:   "exec",
		Usage:  "execute a command in XDS",
		Action: exec,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "id",
				EnvVar: "XDS_PROJECT_ID",
				Usage:  "project ID you want to build (mandatory variable)",
			},
			cli.StringFlag{
				Name:   "rpath, p",
				EnvVar: "XDS_RPATH",
				Usage:  "relative path into project",
			},
			cli.StringFlag{
				Name:   "sdkid, sdk",
				EnvVar: "XDS_SDK_ID",
				Usage:  "Cross Sdk ID to use to build project",
			},
		},
	})
}

func exec(ctx *cli.Context) error {
	prjID := ctx.String("id")
	rPath := ctx.String("rPath")
	sdkid := ctx.String("sdkid")

	// Check mandatory args
	if prjID == "" {
		return cli.NewExitError("project id must be set (see --id option)", 1)
	}

	argsCommand := make([]string, len(ctx.Args()))
	copy(argsCommand, ctx.Args())
	Log.Infof("Execute: /exec %v", argsCommand)

	// Log useful info for debugging
	ver := xaapiv1.XDSVersion{}
	XdsVersionGet(&ver)
	Log.Infof("XDS version: %v", ver)

	// Process Socket IO events
	type exitResult struct {
		error error
		code  int
	}
	exitChan := make(chan exitResult, 1)

	IOsk.On("disconnection", func(err error) {
		Log.Debugf("WS disconnection event with err: %v\n", err)
		exitChan <- exitResult{err, 2}
	})

	outFunc := func(timestamp, stdout, stderr string) {
		tm := ""
		if ctx.Bool("WithTimestamp") {
			tm = timestamp + "| "
		}
		if stdout != "" {
			fmt.Printf("%s%s", tm, stdout)
		}
		if stderr != "" {
			fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
		}
	}

	IOsk.On(xaapiv1.ExecOutEvent, func(ev xaapiv1.ExecOutMsg) {
		outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
	})

	IOsk.On(xaapiv1.ExecExitEvent, func(ev xaapiv1.ExecExitMsg) {
		exitChan <- exitResult{ev.Error, ev.Code}
	})

	IOsk.On(xaapiv1.EVTProjectChange, func(ev xaapiv1.EventMsg) {
		prj, _ := ev.DecodeProjectConfig()
		Log.Infof("Event %v (%v): %v", ev.Type, ev.Time, prj)
	})
	evReg := xaapiv1.EventRegisterArgs{Name: xaapiv1.EVTProjectChange}
	if err := HTTPCli.Post("/events/register", &evReg, nil); err != nil {
		return cli.NewExitError(err, 1)
	}

	// Retrieve the project definition
	prj := xaapiv1.ProjectConfig{}
	if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
		return cli.NewExitError(err, 1)
	}

	// Auto setup rPath if needed
	if rPath == "" {
		cwd, err := os.Getwd()
		if err == nil {
			fldRp := prj.ClientPath
			if !strings.HasPrefix(fldRp, "/") {
				fldRp = "/" + fldRp
			}
			Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
			if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
				rPath = strings.Trim(sp[1], "/")
				Log.Debugf("Auto-setup rPath to: '%s'", rPath)
			}
		}
	}

	// Build env
	Log.Debugf("Command env: %v", EnvConfFileMap)
	env := []string{}
	for k, v := range EnvConfFileMap {
		env = append(env, k+"="+v)
	}

	// Send build command
	args := xaapiv1.ExecArgs{
		ID:         prjID,
		SdkID:      sdkid,
		Cmd:        strings.Trim(argsCommand[0], " "),
		Args:       argsCommand[1:],
		Env:        env,
		RPath:      rPath,
		CmdTimeout: 60,
	}

	LogPost("POST /exec %v", args)
	if err := HTTPCli.Post("/exec", args, nil); err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	// Wait exit
	select {
	case res := <-exitChan:
		errStr := ""
		if res.code == 0 {
			Log.Debugln("Exit successfully")
		}
		if res.error != nil {
			Log.Debugln("Exit with ERROR: ", res.error.Error())
			errStr = res.error.Error()
		}
		return cli.NewExitError(errStr, res.code)
	}
}
5' href='#n975'>975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkProxy>
#include <QTreeWidget>
#include <iostream>
#include <error.h>
#include <json-c/json.h>
#include <stdlib.h>
#include <unistd.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#include "MainApp.h"
#include "Business.h"
#include "InfoPanel.h"
#include "ClickableLabel.h"
#include "Keyboard.h"
#include "traces.h"

#define NAVI_CONFIG_FILEPATH    "/etc/naviconfig.ini"

#define DEFAULT_TEXT        "Select your destination with Yelp !"
#define URL_AUTH            "https://api.yelp.com/oauth2/token"
#define URL_AUTOCOMPLETE    "https://api.yelp.com/v3/autocomplete"
#define URL_SEARCH          "https://api.yelp.com/v3/businesses/search"

#define LEFT_OFFSET         28
#define FONT_SIZE_LINEDIT   20
#define FONT_SIZE_LIST      18
#define TEXT_INPUT_WIDTH    800
#define SEARCH_BTN_SIZE     105
#define SPACER              15
#define WIDGET_WIDTH        (SEARCH_BTN_SIZE + SPACER + TEXT_INPUT_WIDTH)
#define DISPLAY_WIDTH       TEXT_INPUT_WIDTH
#define DISPLAY_HEIGHT      480
#define COMPLETE_W_WITH_KB    1080
#define COMPLETE_H_WITH_KB    1488
#define RESULT_ITEM_HEIGHT  80
#define MARGINS             25
#define AGL_REFRESH_DELAY   75 /* milliseconds */

#define SCROLLBAR_STYLE \
"QScrollBar:vertical {" \
"    border: 2px solid grey;" \
"    background: gray;" \
"    width: 45px;" \
"}"

using namespace std;

MainApp::MainApp():QMainWindow(Q_NULLPTR, Qt::FramelessWindowHint),
    networkManager(this),searchBtn(QIcon(tr(":/images/loupe-90.png")), tr(""), this),
    lineEdit(this),keyboard(QRect(0, 688, COMPLETE_W_WITH_KB, 720), this),
    mutex(QMutex::Recursive),token(""),currentSearchingText(""),currentSearchedText(""),
    pSearchReply(NULL),pInfoPanel(NULL),pResultList(NULL),currentLatitude(36.136261),currentLongitude(-115.151254),
    navicoreSession(0),currentIndex(0),fontId(-1),isInfoScreen(false),
    isInputDisplayed(false),isKeyboard(false),isAglNavi(false)
{
    //this->setAttribute(Qt::WA_TranslucentBackground);
    this->setStyleSheet("border: none;");

    searchBtn.setStyleSheet("border: none; color: #FFFFFF;");
    searchBtn.setMinimumSize(QSize(SEARCH_BTN_SIZE, SEARCH_BTN_SIZE));
    searchBtn.setIconSize(searchBtn.size());
    searchBtn.setGeometry(QRect(LEFT_OFFSET, 0, searchBtn.width(), searchBtn.height()));

    lineEdit.setStyleSheet("border: none; color: #FFFFFF;");
    lineEdit.setMinimumSize(QSize(TEXT_INPUT_WIDTH, SEARCH_BTN_SIZE));

    lineEdit.setPlaceholderText(QString(DEFAULT_TEXT));
    font = lineEdit.font();
    font.setPointSize(FONT_SIZE_LINEDIT);
    lineEdit.setFont(font);
    lineEdit.setTextMargins(MARGINS/2, 0, 0, 0);
    lineEdit.installEventFilter(this);
    lineEdit.setGeometry(QRect(LEFT_OFFSET + searchBtn.width() + SPACER, 0, lineEdit.width(), lineEdit.height()));
    lineEdit.setVisible(false);


    QFile file(NAVI_CONFIG_FILEPATH);
    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QByteArray data = file.readAll();
        QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
        QJsonObject jsonObj(jsonDoc.object());

        if (jsonObj.contains("latitude"))
            currentLatitude = jsonObj["latitude"].toDouble();

        if (jsonObj.contains("longitude"))
            currentLongitude = jsonObj["longitude"].toDouble();
    }

    /* We might need a Japanese font: */
    QFile fontFile(":/fonts/DroidSansJapanese.ttf");
    if (!fontFile.open(QIODevice::ReadOnly))
    {
        TRACE_ERROR("failed to open font file");
    }
    else
    {
        QByteArray fontData = fontFile.readAll();
        fontId = QFontDatabase::addApplicationFontFromData(fontData);
        if (fontId < 0)
        {
            TRACE_ERROR("QFontDatabase::addApplicationFontFromData failed");
        }
    }

    /* Check if "AGL_NAVI" env variable is set. If yes, we must notify
     * AGL environment when surface needs to be resized */
    if (getenv("AGL_NAVI"))
        isAglNavi = true;

    connect(this, SIGNAL(positionGotSignal()), this, SLOT(positionGot()));
    connect(this, SIGNAL(allRoutesGotSignal()), this, SLOT(allRoutesGot()));
    connect(this, SIGNAL(routeCreatedSignal()), this, SLOT(routeCreated()));

    this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
    this->setStyleSheet("background-image: url(:/images/AGL_POI_Background.png);");
    this->show();
}

MainApp::~MainApp()
{
    mutex.lock();
    if (fontId >= 0)
        QFontDatabase::removeApplicationFont(fontId);

    searchBtn.disconnect();
    lineEdit.disconnect();
    networkManager.disconnect();
    keyboard.disconnect();

    delete pSearchReply;
    delete pInfoPanel;
    mutex.unlock();
}

void MainApp::searchBtnClicked()
{
    isInputDisplayed = !isInputDisplayed;
    TRACE_DEBUG("isInputDisplayed = %d", isInputDisplayed);
    DisplayLineEdit(isInputDisplayed);
}

void MainApp::DisplayLineEdit(bool display)
{
    mutex.lock();

    this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));

    if (display)
    {
        lineEdit.setVisible(true);
        lineEdit.setFocus();
    }
    else
    {
        if (pResultList)
        {
            pResultList->removeEventFilter(this);
            delete pResultList;
            pResultList = NULL;
        }
        if (pInfoPanel)
        {
            delete pInfoPanel;
            pInfoPanel = NULL;
        }
        lineEdit.setText(tr(""));
        lineEdit.setVisible(false);
    }
    isInputDisplayed = display;

    mutex.unlock();
}

void MainApp::UpdateAglSurfaces()
{
    char cmd[1024];

    TRACE_DEBUG("handle AGL demo surfaces (new surface is bigger)");
    snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT source region 0 0 %d %d",
        this->width(), this->height());
    TRACE_DEBUG("%s", cmd);
    system(cmd);
    snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT destination region $CLIENT_X $CLIENT_Y %d %d",
        this->width(), this->height());
    TRACE_DEBUG("%s", cmd);
    system(cmd);
}

void MainApp::DisplayResultList(bool display, bool RefreshDisplay)
{
    mutex.lock();

    if (display)
    {
        if (!pResultList)
        {
            pResultList = new QTreeWidget(this);
            pResultList->setStyleSheet("border: none; color: #FFFFFF;");
            pResultList->setRootIsDecorated(false);
            pResultList->setEditTriggers(QTreeWidget::NoEditTriggers);
            pResultList->setSelectionBehavior(QTreeWidget::SelectRows);
            pResultList->setFrameStyle(QFrame::Box | QFrame::Plain);
            pResultList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            //pResultList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            pResultList->setAttribute(Qt::WA_AcceptTouchEvents);
            pResultList->verticalScrollBar()->setStyleSheet(SCROLLBAR_STYLE);
            pResultList->header()->hide();
            //font.setPointSize(FONT_SIZE_LIST);
            //pResultList->setFont(font);
            pResultList->installEventFilter(this);
        }

        pResultList->setGeometry(QRect(   LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
                                        DISPLAY_WIDTH, DISPLAY_HEIGHT));
        if (RefreshDisplay)
        {
            this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
        }
        pResultList->setVisible(true);
        pResultList->setFocus();
    }
    else
    {
        if (pResultList)
        {
            pResultList->removeEventFilter(this);
            pResultList->deleteLater();
            pResultList = NULL;
        }

        lineEdit.setFocus();

        if (RefreshDisplay)
        {
            this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
        }
    }

    mutex.unlock();
}

void MainApp::textChanged(const QString & text)
{
    TRACE_INFO("New text is: %s", qPrintable(text));

    /* do not handle text input if info panel is displayed: */
    if (pInfoPanel) return;

    mutex.lock();

    delete pSearchReply;    /* cancel current search */
    pSearchReply = NULL;

    if (text.length() == 0) /* if empty text -> no search */
    {
        DisplayResultList(false);
        mutex.unlock();
        return;
    }

    /* if text is the same as previous search -> no need to search again */
    if (text == currentSearchedText)
    {
        DisplayResultList(true);
        FillResultList(Businesses, currentIndex);
        mutex.unlock();
        return;
    }
    this->currentSearchingText = text;

    /* we need to know our current position */
    std::vector<int32_t> Params;
    Params.push_back(naviapi::NAVICORE_LONGITUDE);
    Params.push_back(naviapi::NAVICORE_LATITUDE);
    naviapi.getPosition(Params);

    mutex.unlock();
}

void MainApp::textAdded(const QString & text)
{
    mutex.lock();
    lineEdit.setText(lineEdit.text() + text);
    mutex.unlock();
}

void MainApp::keyPressed(int key)
{
    mutex.lock();
    if (key == '\b') /* backspace */
    {
        int len = lineEdit.text().length();
        if (len > 0)
            lineEdit.setText(lineEdit.text().remove(len-1, 1));
    }
    mutex.unlock();
}

void MainApp::itemClicked()
{
    mutex.lock();
    if (isInfoScreen)
    {
        DisplayInformation(true, false);
    }
    else
    {
        SetDestination();
        DisplayLineEdit(false);
    }
    mutex.unlock();
}

void MainApp::ParseJsonBusinessList(const char* buf, std::vector<Business> & Output)
{
    json_object *jobj = json_tokener_parse(buf);
    if (!jobj)
    {
        TRACE_ERROR("json_tokener_parse failed");
        cerr << "json_tokener_parse failed: " << buf << endl;
        return;
    }

    json_object_object_foreach(jobj, key, val)
    {
        (void)key;
        json_object *value;

        if (json_object_get_type(val) == json_type_array)
        {
            TRACE_DEBUG_JSON("an array was found");

            if(json_object_object_get_ex(jobj, "businesses", &value))
            {
                TRACE_DEBUG_JSON("an business was found");

                int arraylen = json_object_array_length(value);

                for (int i = 0; i < arraylen; i++)
                {
                    Business NewBusiness;

                    json_object* medi_array_obj, *medi_array_obj_elem;
                    medi_array_obj = json_object_array_get_idx(value, i);
                    if (medi_array_obj)
                    {
                        if (json_object_object_get_ex(medi_array_obj, "rating", &medi_array_obj_elem))
                        {
                            NewBusiness.Rating = json_object_get_double(medi_array_obj_elem);
                            TRACE_DEBUG_JSON("got Rating : %f", NewBusiness.Rating);
                        }

                        if (json_object_object_get_ex(medi_array_obj, "distance", &medi_array_obj_elem))
                        {
                            NewBusiness.Distance = json_object_get_double(medi_array_obj_elem);
                            TRACE_DEBUG_JSON("got Distance : %f", NewBusiness.Distance);
                        }

                        if (json_object_object_get_ex(medi_array_obj, "review_count", &medi_array_obj_elem))
                        {
                            NewBusiness.ReviewCount = json_object_get_int(medi_array_obj_elem);
                            TRACE_DEBUG_JSON("got ReviewCount : %u", NewBusiness.ReviewCount);
                        }

                        if (json_object_object_get_ex(medi_array_obj, "name", &medi_array_obj_elem))
                        {
                            NewBusiness.Name = QString(json_object_get_string(medi_array_obj_elem));
                            TRACE_DEBUG_JSON("got Name : %s", qPrintable(NewBusiness.Name));
                        }

                        if (json_object_object_get_ex(medi_array_obj, "image_url", &medi_array_obj_elem))
                        {
                            NewBusiness.ImageUrl = QString(json_object_get_string(medi_array_obj_elem));
                            TRACE_DEBUG_JSON("got ImageUrl : %s", qPrintable(NewBusiness.ImageUrl));
                        }

                        if (json_object_object_get_ex(medi_array_obj, "phone", &medi_array_obj_elem))
                        {
                            NewBusiness.Phone = QString(json_object_get_string(medi_array_obj_elem));
                            TRACE_DEBUG_JSON("got Phone : %s", qPrintable(NewBusiness.Phone));
                        }

                        if (json_object_object_get_ex(medi_array_obj, "coordinates", &medi_array_obj_elem))
                        {
                            json_object *value2;

                            TRACE_DEBUG_JSON("coordinates were found");

                            if(json_object_object_get_ex(medi_array_obj_elem, "latitude", &value2))
                            {
                                NewBusiness.Latitude = json_object_get_double(value2);
                                TRACE_DEBUG_JSON("got Latitude : %f", NewBusiness.Latitude);
                            }

                            if(json_object_object_get_ex(medi_array_obj_elem, "longitude", &value2))
                            {
                                NewBusiness.Longitude = json_object_get_double(value2);
                                TRACE_DEBUG_JSON("got Longitude : %f", NewBusiness.Longitude);
                            }
                        }

                        if (json_object_object_get_ex(medi_array_obj, "location", &medi_array_obj_elem))
                        {
                            json_object *value2;

                            TRACE_DEBUG_JSON("a location was found");

                            /* TODO: how do we deal with address2 and address3 ? */
                            if(json_object_object_get_ex(medi_array_obj_elem, "address1", &value2))
                            {
                                NewBusiness.Address = QString(json_object_get_string(value2));
                                TRACE_DEBUG_JSON("got Address : %s", qPrintable(NewBusiness.Address));
                            }

                            if(json_object_object_get_ex(medi_array_obj_elem, "city", &value2))
                            {
                                NewBusiness.City = QString(json_object_get_string(value2));
                                TRACE_DEBUG_JSON("got City : %s", qPrintable(NewBusiness.City));
                            }

                            if(json_object_object_get_ex(medi_array_obj_elem, "state", &value2))
                            {
                                NewBusiness.State = QString(json_object_get_string(value2));
                                TRACE_DEBUG_JSON("got State : %s", qPrintable(NewBusiness.State));
                            }

                            if(json_object_object_get_ex(medi_array_obj_elem, "zip_code", &value2))
                            {
                                NewBusiness.ZipCode = QString(json_object_get_string(value2));
                                TRACE_DEBUG_JSON("got ZipCode : %s", qPrintable(NewBusiness.ZipCode));
                            }

                            if(json_object_object_get_ex(medi_array_obj_elem, "country", &value2))
                            {
                                NewBusiness.Country = QString(json_object_get_string(value2));
                                TRACE_DEBUG_JSON("got Country : %s", qPrintable(NewBusiness.Country));
                            }
                        }

                        /* TODO: parse categories */

                        /* Add business in our list: */
                        Businesses.push_back(NewBusiness);
                    }
                }
            }
        }
    }

    json_object_put(jobj);
}

bool MainApp::eventFilter(QObject *obj, QEvent *ev)
{
    bool ret = false;

    mutex.lock();

    if (obj == pResultList)
    {
        //TRACE_DEBUG("ev->type() = %d", (int)ev->type());

        if (ev->type() == QEvent::KeyPress)
        {
            bool consumed = false;
            int key = static_cast<QKeyEvent*>(ev)->key();
            TRACE_DEBUG("key pressed (%d)", key);
            switch (key) {
                case Qt::Key_Enter:
                case Qt::Key_Return:
                    TRACE_DEBUG("enter or return");
                    if (isInfoScreen)
                    {
                        DisplayInformation(true);
                    }
                    else
                    {
                        SetDestination();
                        DisplayLineEdit(false);
                    }
                    consumed = true;
                    break;

                case Qt::Key_Escape:
                    TRACE_DEBUG("escape");
                    DisplayResultList(false);
                    consumed = true;
                    break;

                case Qt::Key_Up:
                case Qt::Key_Down:
                case Qt::Key_Home:
                case Qt::Key_End:
                case Qt::Key_PageUp:
                case Qt::Key_PageDown:
                    TRACE_DEBUG("arrows");
                    break;

                default:
                    TRACE_DEBUG("default");
                    lineEdit.event(ev);
                    break;
            }

            mutex.unlock();
            return consumed;
        }
    }
    else if (obj == &lineEdit)
    {
        if (pInfoPanel && ev->type() == QEvent::KeyPress)
        {
            switch(static_cast<QKeyEvent*>(ev)->key())
            {
                case Qt::Key_Escape:
                    TRACE_DEBUG("Escape !");
                    DisplayInformation(false, false);
                    DisplayResultList(true);
                    FillResultList(Businesses, currentIndex);
                    break;
                case Qt::Key_Enter:
                case Qt::Key_Return:
                    TRACE_DEBUG("Go !");
                    SetDestination(currentIndex);
                    DisplayLineEdit(false);
                    break;
                default: break;
            }
        }
    }
    else
    {
        ret = QMainWindow::eventFilter(obj, ev);
    }
    mutex.unlock();
    return ret;
}

void MainApp::resizeEvent(QResizeEvent* event)
{
    QMainWindow::resizeEvent(event);
    if (isAglNavi)
    {
        QTimer::singleShot(AGL_REFRESH_DELAY, Qt::CoarseTimer, this, SLOT(UpdateAglSurfaces()));
    }
}

void MainApp::SetDestination(int index)
{
    mutex.lock();

    /* if pResultList exists, take the selected index
     * otherwise, take the index given as parameter */
    if (pResultList)
    {
        QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
        if (SelectedItems.size() > 0)
        {
            /* select the first selected item : */
            index = pResultList->indexOfTopLevelItem(*SelectedItems.begin());
        }
    }

    TRACE_DEBUG("index is: %d", index);

    /* retrieve the coordinates of this item : */
    this->destinationLatitude = Businesses[index].Latitude;
    this->destinationLongitude = Businesses[index].Longitude;

    naviapi.getAllRoutes();

    mutex.unlock();
}

void MainApp::DisplayInformation(bool display, bool RefreshDisplay)
{
    mutex.lock();
    if (display)
    {
        /* pResultList must exist, so that we can retrieve the selected index: */
        if (!pResultList)
        {
            TRACE_ERROR("pResultList is null");
            mutex.unlock();
            return;
        }

        QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
        if (SelectedItems.size() <= 0)
        {
            TRACE_ERROR("no item is selected");
            mutex.unlock();
            return;
        }

        /* select the first selected item : */
        currentIndex = pResultList->indexOfTopLevelItem(*SelectedItems.begin());

        /* Resize window: */
        DisplayResultList(false, false);

        /* Display info for the selected item: */
        QRect rect( LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
                    DISPLAY_WIDTH, DISPLAY_HEIGHT);
        pInfoPanel = new InfoPanel(this, Businesses[currentIndex], rect);

        if (RefreshDisplay)
        {
            this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
        }

        connect(pInfoPanel->getGoButton(),      SIGNAL(clicked(bool)), this, SLOT(goClicked()));
        connect(pInfoPanel->getCancelButton(),  SIGNAL(clicked(bool)), this, SLOT(cancelClicked()));
    }
    else
    {
        if (pInfoPanel)
        {
            pInfoPanel->getGoButton()->disconnect();
            pInfoPanel->getCancelButton()->disconnect();
            delete pInfoPanel;
            pInfoPanel = NULL;
        }
        lineEdit.setFocus();

        if (RefreshDisplay)
        {
            this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
        }
    }

    mutex.unlock();
}

void MainApp::networkReplySearch(QNetworkReply* reply)
{
    QByteArray buf;

    mutex.lock();

    /* memorize the text which gave this result: */
    currentSearchedText = lineEdit.text();

    if (reply->error() == QNetworkReply::NoError)
    {
        // we only handle this callback if it matches the last search request:
        if (reply != pSearchReply)
        {
            TRACE_INFO("this reply is already too late (or about a different network request)");
            mutex.unlock();
            return;
        }

        buf = reply->readAll();

        if (buf.isEmpty())
        {
            mutex.unlock();
            return;
        }



        currentIndex = 0;
        Businesses.clear();
        ParseJsonBusinessList(buf.data(), Businesses);
        DisplayResultList(true);
        FillResultList(Businesses);
    }
    else
    {
        fprintf(stderr,"POI: reply error network please check to poikey and system time (adjusted?)\n");
    }

    mutex.unlock();
}

/* pResultList must be allocated at this point ! */
int MainApp::FillResultList(vector<Business> & list, int focusIndex)
{
    int nbElem = 0;

    mutex.lock();

    pResultList->setUpdatesEnabled(false);
    pResultList->clear();

    /* filling the dropdown menu: */
    for (vector<Business>::iterator it = list.begin(); it != list.end(); it++)
    {
        /*  workaround to avoid entries with wrong coordinates returned by Yelp: */
        if (IsCoordinatesConsistent(*it) == false)
        {
            list.erase(it--);
            continue;
        }

        QTreeWidgetItem * item = new QTreeWidgetItem(pResultList);

        ClickableLabel *label = new ClickableLabel("<b>"+(*it).Name+
            "</b><br>"+(*it).Address+", "+(*it).City+", "+(*it).State+
            " "+(*it).ZipCode+", "+(*it).Country, pResultList);
        label->setTextFormat(Qt::RichText);
        font.setPointSize(FONT_SIZE_LIST);
        label->setFont(font);
        label->setIndent(MARGINS);
        label->setAttribute(Qt::WA_AcceptTouchEvents);
        item->setSizeHint(0, QSize(TEXT_INPUT_WIDTH, RESULT_ITEM_HEIGHT));
        pResultList->setItemWidget(item, 0, label);
        connect(label, SIGNAL(clicked()), this, SLOT(itemClicked()));

        //item->setText(0, (*it).Name);

        if (nbElem == focusIndex)
        {
            pResultList->setCurrentItem(item);
        }
        nbElem++;
    }

    pResultList->setUpdatesEnabled(true);

    mutex.unlock();
    return nbElem;
}

/* Well... some of the POI returned by Yelp have coordinates which are
 * completely inconsistent with the distance at which the POI is
 * supposed to be.
 * https://github.com/Yelp/yelp-fusion/issues/104
 * Let's skip them for the moment: */
#define PI 3.14159265
#define EARTH_RADIUS 6371000
static inline double toRadians(double a) { return a * PI / 180.0; }
bool MainApp::IsCoordinatesConsistent(Business & business)
{
    double lat1 = toRadians(currentLatitude);
    double lon1 = toRadians(currentLongitude);
    double lat2 = toRadians(business.Latitude);
    double lon2 = toRadians(business.Longitude);
    double x = (lon2 - lon1) * cos((lat1 + lat2)/2);
    double y = lat2 - lat1;
    double DistanceFromCoords = EARTH_RADIUS * sqrt(pow(x, 2) + pow(y, 2));

    /* if calculated distance is not between +/- 10% of the announced
     * distance -> skip this POI: */
    if (DistanceFromCoords < business.Distance * 0.9 ||
        DistanceFromCoords > business.Distance * 1.1)
    {
        TRACE_ERROR("Announced distance: %f, calculated distance: %f", business.Distance, DistanceFromCoords);
        return false;
    }

    return true;
}
/* end of workaround */

bool MainApp::CheckNaviApi(int argc, char *argv[])
{
    bool ret = naviapi.connect(argc, argv, this);

    if (ret == true)
    {
        naviapi.getAllSessions();
    }

    return ret;
}

int MainApp::AuthenticatePOI(const QString & CredentialsFile)
{
    char buf[512];
    QString AppId;
    QString AppSecret;
    QString ProxyHostName;
    QString PortStr;
    QString User;
    QString Password;
    int portnum;

    /* First, read AppId and AppSecret from credentials file: */
    FILE* filep = fopen(qPrintable(CredentialsFile), "r");
    if (!filep)
    {
        fprintf(stderr,"Failed to open credentials file \"%s\": %m", qPrintable(CredentialsFile));
        return -1;
    }

    if (!fgets(buf, 512, filep))
    {
        fprintf(stderr,"Failed to read AppId from credentials file \"%s\"", qPrintable(CredentialsFile));
        fclose(filep);
        return -1;
    }
    if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
        buf[strlen(buf)-1] = '\0';
    AppId = QString(buf);

    if (!fgets(buf, 512, filep))
    {
        fprintf(stderr,"Failed to read AppSecret from credentials file \"%s\"", qPrintable(CredentialsFile));
        fclose(filep);
        return -1;
    }
    if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
        buf[strlen(buf)-1] = '\0';
    AppSecret = QString(buf);

    QNetworkProxy proxy;

    //ProxyHostName
    if (!fgets(buf, 512, filep))
    {
        TRACE_INFO("Failed to read ProxyHostName from credentials file \"%s\"", qPrintable(CredentialsFile));
    }
    else
    {
        if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
        ProxyHostName = QString(buf);
        ProxyHostName.replace(0, 14, tr(""));

        //Port
        if (!fgets(buf, 512, filep))
        {
            TRACE_ERROR("Failed to read Port from credentials file \"%s\"", qPrintable(CredentialsFile));
            fclose(filep);
            return -1;
        }
        if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
        PortStr = QString(buf);
        PortStr.replace(0, 5, tr(""));
        portnum = PortStr.toInt();

        //User
        if (!fgets(buf, 512, filep))
        {
            TRACE_ERROR("Failed to read User from credentials file \"%s\"", qPrintable(CredentialsFile));
            fclose(filep);
            return -1;
        }
        if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
        User = QString(buf);
        User.replace(0, 5, tr(""));

        //Password
        if (!fgets(buf, 512, filep))
        {
            TRACE_ERROR("Failed to read Password from credentials file \"%s\"", qPrintable(CredentialsFile));
            fclose(filep);
            return -1;
        }
        if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
        Password = QString(buf);
        Password.replace(0, 9, tr(""));

        proxy.setType(QNetworkProxy::HttpProxy);
        proxy.setHostName(qPrintable(ProxyHostName));
        proxy.setPort(portnum);
        proxy.setUser(qPrintable(User));
        proxy.setPassword(qPrintable(Password));
        QNetworkProxy::setApplicationProxy(proxy);
    }

    fclose(filep);

    TRACE_INFO("Found credentials");

    /* Then, send a HTTP request to get the token and wait for answer (synchronously): */

    token = AppSecret;
    return 0;
}

int MainApp::StartMonitoringUserInput()
{
    connect(&searchBtn, SIGNAL(clicked(bool)), this, SLOT(searchBtnClicked()));
    connect(&lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
    connect(&networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReplySearch(QNetworkReply*)));
    connect(&keyboard, SIGNAL(keyClicked(const QString &)), this, SLOT(textAdded(const QString &)));
    connect(&keyboard, SIGNAL(specialKeyClicked(int)), this, SLOT(keyPressed(int)));
    return 1;
}

void MainApp::SetWayPoints(uint32_t myRoute)
{
    /* set the destination : */
    naviapi::Waypoint destWp(this->destinationLatitude, this->destinationLongitude);
    std::vector<naviapi::Waypoint> myWayPoints;
    myWayPoints.push_back(destWp);
    naviapi.setWaypoints(navicoreSession, myRoute, true, myWayPoints);

    naviapi.calculateRoute(navicoreSession, myRoute);

    /* reset search: */
    currentSearchingText = tr("");
    currentSearchedText = tr("");
    currentIndex = 0;
    Businesses.clear();
}

void MainApp::goClicked()
{
    TRACE_DEBUG("Go clicked !");
    SetDestination(currentIndex);
    DisplayLineEdit(false);
}

void MainApp::cancelClicked()
{
    TRACE_DEBUG("Cancel clicked !");
    DisplayInformation(false, false);
    DisplayResultList(true, false);
    FillResultList(Businesses, currentIndex);
}

void MainApp::getAllSessions_reply(const std::map< uint32_t, std::string >& allSessions)
{
    mutex.lock();

    if (allSessions.empty())
    {
        TRACE_ERROR("Error: could not find an instance of Navicore");
        mutex.unlock();
        return;
    }

    this->navicoreSession = allSessions.begin()->first;

    TRACE_INFO("Current session: %d", this->navicoreSession);

    mutex.unlock();
}


void MainApp::getPosition_reply(std::map< int32_t, naviapi::variant > position)
{
    mutex.lock();

    std::map< int32_t, naviapi::variant >::iterator it;
    for (it = position.begin(); it != position.end(); it++)
    {
        if (it->first == naviapi::NAVICORE_LATITUDE)
        {
            currentLatitude = it->second._double;
        }
        else if (it->first == naviapi::NAVICORE_LONGITUDE)
        {
            currentLongitude = it->second._double;
        }
    }

    TRACE_INFO("Current position: %f, %f", currentLatitude, currentLongitude);

    mutex.unlock();

    emit positionGotSignal();
}

void MainApp::getAllRoutes_reply(std::vector< uint32_t > allRoutes)
{
    mutex.lock();

    uint32_t routeHandle = 0;

    if (allRoutes.size() != 0)
    {
        routeHandle = allRoutes[0];
    }

    this->currentRouteHandle = routeHandle;

    mutex.unlock();

    emit allRoutesGotSignal();
}

void MainApp::createRoute_reply(uint32_t routeHandle)
{
    mutex.lock();

    this->currentRouteHandle = routeHandle;

    mutex.unlock();

    emit routeCreatedSignal();
}

void MainApp::positionGot()
{
    mutex.lock();

    /* let's generate a search request : */
    QString myUrlStr = URL_SEARCH + tr("?") + tr("term=") + currentSearchingText +
        tr("&latitude=") + QString::number(currentLatitude) +
        tr("&longitude=") + QString::number(currentLongitude);

    TRACE_DEBUG("URL: %s", qPrintable(myUrlStr));

    QUrl myUrl = QUrl(myUrlStr);
    QNetworkRequest req(myUrl);
    req.setRawHeader(QByteArray("Authorization"), (tr("bearer ") + token).toLocal8Bit());

    /* Then, send a HTTP request to get the token and wait for answer (synchronously): */

    pSearchReply = networkManager.get(req);

    mutex.unlock();
}

void MainApp::allRoutesGot()
{
    mutex.lock();

    /* check if a route already exists, if not create it : */
    if (this->currentRouteHandle == 0)
    {
        naviapi.createRoute(navicoreSession);
    }
    else
    {
        naviapi.pauseSimulation(navicoreSession);
        naviapi.setSimulationMode(navicoreSession, false);
        naviapi.cancelRouteCalculation(navicoreSession, this->currentRouteHandle);
        sleep(1);

        SetWayPoints(this->currentRouteHandle);
    }

    mutex.unlock();
}

void MainApp::routeCreated()
{
    mutex.lock();

    SetWayPoints(this->currentRouteHandle);

    mutex.unlock();
}