/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**/
#include <QtQmlDom/private/qqmldompath_p.h>
#include <QtQmlDom/private/qqmldomitem_p.h>

#include <QtTest/QtTest>

QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
namespace PathEls {

class TestPaths: public QObject {
    Q_OBJECT
public:
    void testPathInternals(Path p1)
    {
        QCOMPARE(p1.component(0).kind(), Kind::Root);
        QCOMPARE(p1.component(1).kind(), Kind::Current);

        Path p11 = Path::field(u"test");
        QString s = QLatin1String("test");
        Path p2 = Path::field(s);
        Path p3 = Path::field(QLatin1String("test"));
        QCOMPARE(p11, p2);
        QCOMPARE(p11, p3);
        QVERIFY(p11.m_data->strData.isEmpty());
        QCOMPARE(p2.m_data->strData.length(), 1);
        QCOMPARE(p2.m_data->strData.first(), s);
        QCOMPARE(p3.m_data->strData.length(), 1);
        QCOMPARE(p3.m_data->strData.first(), s);
    }

private slots:
    void pathComponentTestInternalAlloc() {
        PathComponent c;
        QCOMPARE(c.kind(), Kind::Empty);
        PathComponent c1{Current()};
        QCOMPARE(c1.kind(), Kind::Current);
        QVERIFY(c!=c1);
        QVERIFY(c<c1);
        QVERIFY(c1>c);
        PathComponent c1_1{Current(PathCurrent::Ids)};
        QCOMPARE(c1_1.kind(), Kind::Current);
        QVERIFY(c1 != c1_1);
        QVERIFY(c < c1_1);
        QCOMPARE(c1_1.name(), QLatin1String("@ids"));
        PathComponent c1_2{Current(u"ids")};
        QCOMPARE(c1_1, c1_2);
        PathComponent c2 = c1;
        QCOMPARE(c2.kind(), Kind::Current);
        QCOMPARE(c2, c1);
        PathComponent c3;
        QCOMPARE(c, c3);
        QCOMPARE(c3.kind(), Kind::Empty);
        c3 = c1;
        QCOMPARE(c3.kind(), Kind::Current);
        QCOMPARE(c3, c1);
        PathComponent c4{Field(u"bla")};
        QCOMPARE(c4.kind(), Kind::Field);
        QCOMPARE(c4.name(), QLatin1String("bla"));
        auto c5=PathComponent(Index(42));
        QCOMPARE(c5.kind(), Kind::Index);
        QCOMPARE(c5.index(), 42);
        auto c6=PathComponent(Key(u"bla"));
        QCOMPARE(c6.kind(), Kind::Key);
        QCOMPARE(c6.name(), QLatin1String("bla"));
        auto c7=PathComponent(Key(u" ugly\n \t \\string\"'bla"));
        QCOMPARE(c7.kind(), Kind::Key);
        QCOMPARE(c7.name(), QLatin1String(" ugly\n \t \\string\"'bla"));
        auto c8=PathComponent(Root(u"pippo"));
        QCOMPARE(c8.kind(), Kind::Root);
        QCOMPARE(c8.name(), QLatin1String("$pippo"));
        auto c8_1=PathComponent(Root(PathRoot::Env));
        QCOMPARE(c8_1.kind(), Kind::Root);
        QCOMPARE(c8_1.name(), QLatin1String("$env"));
        auto c8_2=PathComponent(Root(u"env"));
        QCOMPARE(c8_1, c8_2);
        auto c9=PathComponent(Current(u"ippo"));
        QCOMPARE(c9.kind(), Kind::Current);
        QCOMPARE(c9.name(), QLatin1String("@ippo"));
        auto c10=PathComponent(Any());
        QCOMPARE(c10.kind(), Kind::Any);
        QVERIFY(c9!=c10);
        auto c11=PathComponent(Filter([](DomItem){ return true; }));
        auto c12=c11;
        auto c13=PathComponent(Filter([](DomItem){ return false; }));
        auto c14=PathComponent(Filter([](DomItem){ return false; }, u"skipAll"));
        auto c15=PathComponent(Filter([](DomItem){ return true; }, u"skipAll"));
        QCOMPARE(c11.kind(), Kind::Filter);
        QCOMPARE(c11, c11);
        QVERIFY(c11 != c12); // native code assumed to be non comparable and different even if they are the same
        QVERIFY(c11 != c13);
        QVERIFY(c13 != c14);
        QVERIFY(c11 != c14);
        QCOMPARE(c14, c14);
        QCOMPARE(c14, c15); // same description (without < at the beginning) assumes same function even if different
    }

    void testPaths() {
        Path p;
        QCOMPARE(p.length(), 0);
        QCOMPARE(p.length(), 0);
        Path p0 = Path::root();
        QCOMPARE(p0[0].headKind(), Kind::Root);
        QCOMPARE(p0.length(), 1);
        Path p1 = p0.subCurrent();
        QCOMPARE(p1.length(), 2);
        testPathInternals(p1);
        QCOMPARE(p1[0].headKind(), Kind::Root);
        QCOMPARE(p1[1].headKind(), Kind::Current);
        auto p2 = p1.subField(u"aa");
        QCOMPARE(p2[2].headKind(), Kind::Field);
        auto p3 = p2.subIndex(4);
        QCOMPARE(p3[3].headKind(), Kind::Index);
        auto p4 = p3.subKey("bla");
        QCOMPARE(p4[4].headKind(), Kind::Key);
        auto p5 = p4.subAny();
        QCOMPARE(p5[5].headKind(), Kind::Any);
        auto p6 = p5.subEmpty();
        QCOMPARE(p6[6].headKind(), Kind::Empty);
        auto rString = u"$.@.aa[4][\"bla\"][*].";
        QCOMPARE(p6.toString(), rString);
        auto p7 = p6.subFilter([](DomItem){ return true; }, u"true");
        auto p7Str = p7.toString();
        QCOMPARE(p7Str, u"$.@.aa[4][\"bla\"][*].[?(true)]");
        auto p8 = p7.dropTail();
        QCOMPARE(p8.length(), 7);
        QCOMPARE(p8.toString(), rString);
        QCOMPARE(p8, p6);
        auto p9 = Path::fromString(rString);
        QCOMPARE(p9.length(), 7);
        auto p9Str = p9.toString();
        QCOMPARE(p9Str, rString);
        QCOMPARE(p9, p6);
        auto p10 = p6.dropFront();
        QCOMPARE(p10.length(), 6);
        auto p10Str = p10.toString();
        auto r2Str = u"@.aa[4][\"bla\"][*].";
        QCOMPARE(p10Str, r2Str);
        auto p11 = Path::fromString(r2Str);
        auto p11Str = p11.toString();
        QCOMPARE(p11Str, r2Str);
        QCOMPARE(p10, p11);
        auto p12 = p7.mid(1,6);
        auto p12Str = p12.toString();
        QCOMPARE(p12Str, r2Str);
        QCOMPARE(p10, p12);
    }

    void testPathSplit()
    {
        QList<Path> paths({Path(),
            Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto").subIndex(4),
            Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto"),
            Path::root(PathRoot::Env).subField(u"pippo"),
            Path::root(PathRoot::Env).subField(u"pippo").subField(u"pp"),
            Path::root(PathRoot::Env),
            Path::field(u"pippo").subIndex(4),
            Path::field(u"pippo").subKey(u"pluto").subIndex(4),
            Path::field(u"pippo").subKey(u"pluto"),
            Path::field(u"pippo"),
            Path::field(u"pippo").subField(u"pp"),
            Path::index(4),
            Path::key(u"zz")
            });
        foreach (Path p, paths) {
            Source s = p.split();
            QCOMPARE(p, s.pathToSource.subPath(s.pathFromSource));
            if (!s.pathFromSource)
                QVERIFY(!s.pathToSource);
        }
        QCOMPARE(paths.at(1).split().pathToSource, Path::root(PathRoot::Env));
        QCOMPARE(paths.at(2).split().pathToSource, Path::root(PathRoot::Env));
        QCOMPARE(paths.at(3).split().pathToSource, Path::root(PathRoot::Env));
        QCOMPARE(paths.at(4).split().pathToSource, Path::root(PathRoot::Env).subField(u"pippo"));
        QVERIFY(!paths.at(5).split().pathToSource);
        QVERIFY(!paths.at(6).split().pathToSource);
        QVERIFY(!paths.at(7).split().pathToSource);
        QVERIFY(!paths.at(8).split().pathToSource);
        QVERIFY(!paths.at(9).split().pathToSource);
        QCOMPARE(paths.at(10).split().pathToSource, Path::field(u"pippo"));
        QVERIFY(!paths.at(11).split().pathToSource);
        QVERIFY(!paths.at(12).split().pathToSource);
    }
};

} // namespace PathEls
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE

QTEST_MAIN(QQmlJS::Dom::PathEls::TestPaths)
#include "tst_qmldompath.moc"
