@SuppressWarnings("unused")
public class Journey implements PostProcessingEnabler.PostProcessable {

    private String journeyDepartureStationId;
    private String journeyArrivalStationId;
    private List<Solution> solutions;

    public String getJourneyDepartureStationId() {
        return journeyDepartureStationId;
    }

    public void setJourneyDepartureStationId(String journeyDepartureStationId) {
        this.journeyDepartureStationId = journeyDepartureStationId;
    }

    public String getJourneyArrivalStationId() {
        return journeyArrivalStationId;
    }

    public void setJourneyArrivalStationId(String journeyArrivalStationId) {
        this.journeyArrivalStationId = journeyArrivalStationId;
    }

    public List<Solution> getSolutions() {
        return solutions;
    }

    public void setSolutions(List<Solution> solutions) {
        this.solutions = solutions;
    }

    @Override
    public String toString() {
        return "Journey{" +
                "journeyDepartureStationId='" + journeyDepartureStationId + '\'' +
                ", journeyArrivalStationId='" + journeyArrivalStationId + '\'' +
                ", solutions=" + solutions +
                '}';
    }

    @Override
    public void postProcess() {

    }

    public class Solution implements PostProcessingEnabler.PostProcessable {

        private String trainCategory;
        private String trainId;

        private String trainDepartureStationId;
        private String departureStationId;
        private String departureStationName;
        private DateTime departureTime;
        private String departureTimeReadable;
        private String departureTimeWithDelayReadable;
        private String departurePlatform;

        private String arrivalStationId;
        private String arrivalStationName;
        private DateTime arrivalTime;
        private String arrivalTimeWithDelayReadable;
        private String arrivalTimeReadable;

        private Integer timeDifference;
        private Integer progress;

        private String duration;
        private boolean arrivesFirst;
        private boolean hasChanges;
        private List<Change> changesList;

        private Integer trainStatusCode;

        public String getTrainCategory() {
            return trainCategory;
        }

        public void setTrainCategory(String trainCategory) {
            this.trainCategory = trainCategory;
        }

        public String getTrainId() {
            return trainId;
        }

        public void setTrainId(String trainId) {
            this.trainId = trainId;
        }

        public String getTrainDepartureStationId() {
            return trainDepartureStationId;
        }

        public void setTrainDepartureStationId(String trainDepartureStationId) {
            this.trainDepartureStationId = trainDepartureStationId;
        }

        public String getDepartureStationId() {
            return departureStationId;
        }

        public String getDepartureStationShortId() {
            return departureStationId.replaceAll("(S|N)0+|(S|N)", "");
        }

        public void setDepartureStationId(String departureStationId) {
            this.departureStationId = departureStationId;
        }

        public String getDepartureStationName() {
            return departureStationName;
        }

        public void setDepartureStationName(String departureStationName) {
            this.departureStationName = departureStationName;
        }

        public DateTime getDepartureTime() {
            return departureTime;
        }

        public void setDepartureTime(DateTime departureTime) {
            this.departureTime = departureTime;
        }

        public String getDepartureTimeReadable() {
            return departureTimeReadable;
        }

        public void setDepartureTimeReadable(String departureTimeReadable) {
            this.departureTimeReadable = departureTimeReadable;
        }

        public String getDepartureTimeWithDelayReadable() {
            return departureTimeWithDelayReadable;
        }

        public void setDepartureTimeWithDelayReadable(String departureTimeWithDelayReadable) {
            this.departureTimeWithDelayReadable = departureTimeWithDelayReadable;
        }

        public String getDeparturePlatform() {
            return departurePlatform;
        }

        public void setDeparturePlatform(String departurePlatform) {
            this.departurePlatform = departurePlatform;
        }

        public String getArrivalStationName() {
            return arrivalStationName;
        }

        public void setArrivalStationName(String arrivalStationName) {
            this.arrivalStationName = arrivalStationName;
        }

        public String getArrivalStationId() {
            return arrivalStationId;
        }

        public String getArrivalStationShortId() {
            return arrivalStationId.replaceAll("(S|N)0+|(S|N)", "");
        }

        public void setArrivalStationId(String arrivalStationId) {
            this.arrivalStationId = arrivalStationId;
        }

        public DateTime getArrivalTime() {
            return arrivalTime;
        }

        public void setArrivalTime(DateTime arrivalTime) {
            this.arrivalTime = arrivalTime;
        }

        public String getArrivalTimeReadable() {
            return arrivalTimeReadable;
        }

        public void setArrivalTimeReadable(String arrivalTimeReadable) {
            this.arrivalTimeReadable = arrivalTimeReadable;
        }

        public String getArrivalTimeWithDelayReadable() {
            return arrivalTimeWithDelayReadable;
        }

        public void setArrivalTimeWithDelayReadable(String arrivalTimeWithDelayReadable) {
            this.arrivalTimeWithDelayReadable = arrivalTimeWithDelayReadable;
        }

        public Integer getTimeDifference() {
            return timeDifference;
        }

        public void setTimeDifference(Integer timeDifference) {
            this.timeDifference = timeDifference;
        }

        public Integer getProgress() {
            return progress;
        }

        public void setProgress(Integer progress) {
            this.progress = progress;
        }

        public String getDuration() {
            return duration;
        }

        public void setDuration(String duration) {
            this.duration = duration;
        }

        public boolean isArrivesFirst() {
            return arrivesFirst;
        }

        public void setArrivesFirst(boolean arrivesFirst) {
            this.arrivesFirst = arrivesFirst;
        }

        public boolean hasChanges() {
            return hasChanges;
        }

        public void setHasChanges(boolean hasChanges) {
            this.hasChanges = hasChanges;
        }

        public List<Change> getChangesList() {
            return changesList;
        }

        public void setChangesList(List<Change> changesList) {
            this.changesList = changesList;
        }

        public Integer getTrainStatusCode() {
            return trainStatusCode;
        }

        public void setTrainStatusCode(Integer trainStatusCode) {
            this.trainStatusCode = trainStatusCode;
        }

        private String makeDateTimeReadable(DateTime dt) {
            return dt != null ? dt.toString("HH:mm") : "";
        }

        public void refreshData() {
            if (hasChanges) {
                boolean foundAny = false;
                timeDifference = 0;
                for (int i = 0; i < changesList.size(); i++) {
                    if (changesList.get(i).getTimeDifference() != null) {
                        timeDifference += changesList.get(i).getTimeDifference();
                        foundAny = true;
                    }
                }
                if (!foundAny) timeDifference = null;
            }
            postProcess();
        }

        @Override
        public void postProcess() {
            departureTimeReadable = makeDateTimeReadable(departureTime);
            departureTimeWithDelayReadable = makeDateTimeReadable(departureTime.plusMinutes(timeDifference != null ? timeDifference : 0));
            arrivalTimeReadable = makeDateTimeReadable(arrivalTime);
            arrivalTimeWithDelayReadable = makeDateTimeReadable(arrivalTime.plusMinutes(timeDifference != null ? timeDifference : 0));
        }

        @Override
        public String toString() {
            return "Solution{" +
                    "trainCategory='" + trainCategory + '\'' +
                    ", trainId='" + trainId + '\'' +
                    ", trainDepartureStationId='" + trainDepartureStationId + '\'' +
                    ", departureStationName='" + departureStationName + '\'' +
                    ", departureTime=" + departureTime +
                    ", departureTimeReadable='" + departureTimeReadable + '\'' +
                    ", departurePlatform='" + departurePlatform + '\'' +
                    ", arrivalStationName='" + arrivalStationName + '\'' +
                    ", arrivalTime=" + arrivalTime +
                    ", arrivalTimeReadable='" + arrivalTimeReadable + '\'' +
                    ", timeDifference=" + timeDifference +
                    ", progress=" + progress +
                    ", duration='" + duration + '\'' +
                    ", arrivesFirst=" + arrivesFirst +
                    ", hasChanges=" + hasChanges +
                    ", changesList=" + changesList +
                    '}';
        }

        public class Change implements PostProcessingEnabler.PostProcessable {

            private String trainCategory;
            private String trainId;

            private String trainDepartureStationId;
            private String departureStationId;
            private String departureStationName;
            private DateTime departureTime;
            private String departureTimeReadable;
            private String departureTimeWithDelayReadable;
            private String departurePlatform;

            private String arrivalStationId;
            private String arrivalStationName;
            private DateTime arrivalTime;
            private String arrivalTimeReadable;
            private String arrivalTimeWithDelayReadable;

            private Integer timeDifference;
            private Integer progress;

            private String duration;

            public String getTrainCategory() {
                return trainCategory;
            }

            public void setTrainCategory(String trainCategory) {
                this.trainCategory = trainCategory;
            }

            public String getTrainId() {
                return trainId;
            }

            public void setTrainId(String trainId) {
                this.trainId = trainId;
            }

            public String getTrainDepartureStationId() {
                return trainDepartureStationId;
            }

            public void setTrainDepartureStationId(String trainDepartureStationId) {
                this.trainDepartureStationId = trainDepartureStationId;
            }

            public String getDepartureStationId() {
                return departureStationId;
            }

            public void setDepartureStationId(String departureStationId) {
                this.departureStationId = departureStationId;
            }

            public String getDepartureStationName() {
                return departureStationName;
            }

            public void setDepartureStationName(String departureStationName) {
                this.departureStationName = departureStationName;
            }

            public DateTime getDepartureTime() {
                return departureTime;
            }

            public void setDepartureTime(DateTime departureTime) {
                this.departureTime = departureTime;
            }

            public String getDepartureTimeReadable() {
                return departureTimeReadable;
            }

            public void setDepartureTimeReadable(String departureTimeReadable) {
                this.departureTimeReadable = departureTimeReadable;
            }

            public String getDepartureTimeWithDelayReadable() {
                return departureTimeWithDelayReadable;
            }

            public void setDepartureTimeWithDelayReadable(String departureTimeWithDelayReadable) {
                this.departureTimeWithDelayReadable = departureTimeWithDelayReadable;
            }

            public String getDeparturePlatform() {
                return departurePlatform;
            }

            public void setDeparturePlatform(String departurePlatform) {
                this.departurePlatform = departurePlatform;
            }

            public String getArrivalStationId() {
                return arrivalStationId;
            }

            public void setArrivalStationId(String arrivalStationId) {
                this.arrivalStationId = arrivalStationId;
            }

            public String getArrivalStationName() {
                return arrivalStationName;
            }

            public void setArrivalStationName(String arrivalStationName) {
                this.arrivalStationName = arrivalStationName;
            }

            public DateTime getArrivalTime() {
                return arrivalTime;
            }

            public void setArrivalTime(DateTime arrivalTime) {
                this.arrivalTime = arrivalTime;
            }

            public String getArrivalTimeReadable() {
                return arrivalTimeReadable;
            }

            public void setArrivalTimeReadable(String arrivalTimeReadable) {
                this.arrivalTimeReadable = arrivalTimeReadable;
            }

            public String getArrivalTimeWithDelayReadable() {
                return arrivalTimeWithDelayReadable;
            }

            public void setArrivalTimeWithDelayReadable(String arrivalTimeWithDelayReadable) {
                this.arrivalTimeWithDelayReadable = arrivalTimeWithDelayReadable;
            }

            public Integer getTimeDifference() {
                return timeDifference;
            }

            public void setTimeDifference(Integer timeDifference) {
                this.timeDifference = timeDifference;
            }

            public Integer getProgress() {
                return progress;
            }

            public void setProgress(Integer progress) {
                this.progress = progress;
            }

            public String getDuration() {
                return duration;
            }

            public void setDuration(String duration) {
                this.duration = duration;
            }

            private String makeDateTimeReadable(DateTime dt) {
                return dt != null ? dt.toString("HH:mm") : "";
            }

            @Override
            public void postProcess() {
                departureTimeReadable = makeDateTimeReadable(departureTime);
                departureTimeWithDelayReadable = makeDateTimeReadable(departureTime.plusMinutes(timeDifference != null ? timeDifference : 0));
                arrivalTimeReadable = makeDateTimeReadable(arrivalTime);
                arrivalTimeWithDelayReadable = makeDateTimeReadable(arrivalTime.plusMinutes(timeDifference != null ? timeDifference : 0));
            }

            @Override
            public String toString() {
                return "Change{" +
                        "trainCategory='" + trainCategory + '\'' +
                        ", trainId='" + trainId + '\'' +
                        ", trainDepartureStationId='" + trainDepartureStationId + '\'' +
                        ", departureStationName='" + departureStationName + '\'' +
                        ", departureTime=" + departureTime +
                        ", departurePlatform='" + departurePlatform + '\'' +
                        ", arrivalStationName='" + arrivalStationName + '\'' +
                        ", arrivalTime=" + arrivalTime +
                        ", timeDifference=" + timeDifference +
                        ", progress=" + progress +
                        ", duration='" + duration + '\'' +
                        '}';
            }

        }
    }
} public class DateTimeAdapter implements JsonDeserializer<DateTime>, JsonSerializer<DateTime> {
    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Timestamp format
        // "2012-06-11T20:06+00200" format
        return DateTime.parse(json.getAsString());
    }

    @Override
    public JsonElement serialize(DateTime time, Type typeOfT, JsonSerializationContext context) {
        return new JsonPrimitive(time.toString("yyyy-MM-dd'T'HH:mmZ"));
    }
} public class PostProcessingEnabler implements TypeAdapterFactory {

    public interface PostProcessable {
        void postProcess();
    }

    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);

        return new TypeAdapter<T>() {
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            public T read(JsonReader in) throws IOException {
                T obj = delegate.read(in);
                if (obj instanceof PostProcessable) {
                    ((PostProcessable) obj).postProcess();
                }
                return obj;
            }
        };
    }
} public class TrainDetailsActivity extends AppCompatActivity implements
        TrainDetailsContract.View,
        IabHelper.QueryInventoryFinishedListener {

    TrainDetailsContract.Presenter presenter;
    AnalyticsHelper                analyticsHelper;
    BroadcastReceiver              messageReceiver;
    CustomIABHelper                iabHelper;

    MenuItem menuItem;

    @BindView(R.id.toolbar)
    Toolbar toolbar;

    @BindView(R.id.loading_spinner)
    ProgressBar          progressBar;
    @BindView(R.id.rv_train_details)
    RecyclerView         rvTrainDetails;
    @BindView(R.id.btn_refresh)
    ImageButton          btnRefresh;
    @BindView(R.id.btn_share_single)
    FloatingActionButton btnShareSingle;
    @BindView(R.id.btn_share_multiple)
    FloatingActionMenu   btnShareMultiple;

    @BindView(R.id.rl_error)
    RelativeLayout rlEmptyJourneyBox;
    @BindView(R.id.tv_error_message)
    TextView       tvErrorMessage;
    @BindView(R.id.btn_error_button)
    Button         btnErrorMessage;

    @BindView(R.id.rl_banner_placeholder)
    RelativeLayout rlBannerPlaceholder;
    @BindView(R.id.cv_tip_placeholder)
    CardView       cvTipPlaceholder;
    @BindView(adView2)
    AdView         adView;

    TrainDetailsAdapter adapter;
    private long refreshBtnLastClickTime = SystemClock.elapsedRealtime();


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_train_details);
        ButterKnife.bind(this);
        analyticsHelper = AnalyticsHelper.getInstance(getViewContext());
        iabHelper = CustomIABHelper.getInstance(TrainDetailsActivity.this, this);
        Ads.initializeAds(getViewContext(), rlBannerPlaceholder, adView, analyticsHelper, SCREEN_SOLUTION_DETAILS);

        presenter = new TrainDetailsPresenter(this);
        presenter.setState(getIntent().getExtras());

        setSupportActionBar(toolbar);

        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }

        adapter = new TrainDetailsAdapter(getApplicationContext(), presenter);
        rvTrainDetails.setAdapter(adapter);
        rvTrainDetails.setHasFixedSize(true);
        rvTrainDetails.setLayoutManager(new WrapContentLinearLayoutManager(getViewContext(), LinearLayoutManager.VERTICAL, false));
        rvTrainDetails.addOnScrollListener(new HideShowScrollListener() {
            @Override
            public void onHide() {
                btnRefresh.animate().setInterpolator(new LinearInterpolator()).translationY(600).setDuration(100);
            }

            @Override
            public void onShow() {
                btnRefresh.animate().setInterpolator(new LinearInterpolator()).translationY(0).setDuration(100);
            }
        });

        btnRefresh.setOnClickListener(v -> {
            if (SystemClock.elapsedRealtime() - refreshBtnLastClickTime > 1000) {
                AnimationUtils.animOnPress(v, AnimationUtils.ANIM_TYPE.LIGHT);
                analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ACTION_REFRESH_SOLUTION);
                presenter.refreshRequested();
            }
            refreshBtnLastClickTime = SystemClock.elapsedRealtime();
        });

        messageReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                showSnackbar(intent.getExtras().getString(NotificationService.NOTIFICATION_ERROR_MESSAGE), NONE, Snackbar.LENGTH_SHORT);
            }
        };

        presenter.updateRequested();

        if (presenter.isOnlyTrainSearch()) {
            apply(cvTipPlaceholder, VISIBLE);
        } else {
            apply(cvTipPlaceholder, GONE);
        }

        if (ignoreBeingProAndShowAds) {
            loadAds();
        }
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.fav_train_menu, menu);
        if (!this.presenter.isOnlyTrainSearch()) {
            this.menuItem = menu.findItem(R.id.action_fav_train);
            setFavouriteButtonStatus(presenter.isSolutionPreferred());
        } else {
            menu.findItem(R.id.action_fav_train).setVisible(false);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case R.id.action_fav_train:
                presenter.onFavouriteButtonClick();
                break;
            case android.R.id.home:
                if (isTaskRoot()) {
                    Intent i = new Intent(this, FavouriteJourneysActivity.class);
                    startActivity(i);
                }
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(presenter.getState(outState));
    }

    @Override
    protected void onResume() {
        super.onResume();
//        adView.resume();
        iabHelper.isUserPro(this);
        LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, new IntentFilter(NotificationService.NOTIFICATION_ERROR_EVENT));
        presenter.setState(getIntent().getExtras());
    }

    @Override
    protected void onPause() {
//        adView.pause();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        presenter.unsubscribe();
        super.onDestroy();
    }

    @Override
    public Context getViewContext() {
        return TrainDetailsActivity.this;
    }

    @Override
    public void setFavouriteButtonStatus(boolean isPreferred) {
        if (isPreferred) {
            analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ACTION_SET_FAVOURITE);
            this.menuItem.setIcon(R.drawable.ic_star_black);
//            AnimationUtils.animOnPress(this.menuItem, AnimationUtils.ANIM_TYPE.MEDIUM);
        } else {
            analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ACTION_REMOVE_FAVOURITE);
            this.menuItem.setIcon(R.drawable.ic_star_border);
        }
    }

    @Override
    public void showSnackbar(String message, ENUM_SNACKBAR_ACTIONS intent, int duration) {
        Log.w(message);
        Snackbar snackbar = Snackbar
                .make(rvTrainDetails, message, duration);
        ((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setTextColor(ContextCompat.getColor(this, R.color.txt_white));
        switch (intent) {
            case NONE:
                break;
            case REFRESH:
                snackbar.setAction("Aggiorna", view -> presenter.refreshRequested()).setActionTextColor(ContextCompat.getColor(this, R.color.btn_cyan));
                break;
            default:
                break;
        }
        snackbar.show();
    }

    @SuppressWarnings("RestrictedApi")
    @Override
    public void setShareButton(List<String> trainCatAndId) {
        if (trainCatAndId.size() == 1) {
            apply(btnShareSingle, VISIBLE);
            btnShareSingle.setImageDrawable(AppCompatDrawableManager.get().getDrawable(TrainDetailsActivity.this, R.drawable.ic_share));
            btnShareSingle.setOnClickListener(v -> {
                Intent sendIntent = new Intent();
                sendIntent.setAction(Intent.ACTION_SEND);
                sendIntent.putExtra(Intent.EXTRA_TEXT, presenter.getShareableString());
                sendIntent.setType("text/plain");
                startActivity(Intent.createChooser(sendIntent, "Condividi l'andamento del Treno via..."));
            });
        } else {
            apply(btnShareMultiple, VISIBLE);
            btnShareMultiple.removeAllMenuButtons();
            btnShareMultiple.setClosedOnTouchOutside(true);
            for (String s : trainCatAndId) {
                FloatingActionButton menuItem = new FloatingActionButton(TrainDetailsActivity.this);
                menuItem.setLabelText(s);
                menuItem.setButtonSize(FloatingActionButton.SIZE_MINI);
                menuItem.setImageDrawable(AppCompatDrawableManager.get().getDrawable(TrainDetailsActivity.this, R.drawable.ic_share));
                menuItem.setColorNormalResId(R.color.btn_cyan);
                menuItem.setOnClickListener(v -> {
                    Intent sendIntent = new Intent();
                    sendIntent.setAction(Intent.ACTION_SEND);
                    sendIntent.putExtra(Intent.EXTRA_TEXT, presenter.getShareableString(trainCatAndId.indexOf(s)));
                    sendIntent.setType("text/plain");
                    startActivity(Intent.createChooser(sendIntent, "Condividi l'andamento del Treno via..."));
                });
                btnShareMultiple.addMenuButton(menuItem);
            }
        }
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        rvTrainDetails.setVisibility(View.GONE);
        rlEmptyJourneyBox.setVisibility(View.GONE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
        rvTrainDetails.setVisibility(View.VISIBLE);
        rlEmptyJourneyBox.setVisibility(View.GONE);
    }

    @Override
    public void updateTrainDetails() {
        rvTrainDetails.getRecycledViewPool().clear();
        try {
            adapter.notifyDataSetChanged();
        } catch (Exception e) {
            FirebaseCrash.report(new Exception("CATCHED Inconsistency detected. Invalid view holder adapter positionViewHolder"));
        }
    }

    @Override
    public void showNewsDialog(News news) {
        analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ACTION_SHOW_NEWS_DIALOG);
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
        alertDialog.setTitle("Informazioni sul ritardo");
        alertDialog.setMessage(news.getMessage());
        alertDialog.setPositiveButton("OK", (dialog, which) -> dialog.dismiss());
        alertDialog.create().show();
    }

    @Override
    public void showAddSolutionAndJourneyToFavouritesDialog() {
        analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ACTION_SET_FAVOURITE_WITH_JOURNEY);
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
        alertDialog.setTitle("Attenzione!");
        alertDialog.setMessage("Verrà aggiunta anche la tratta cercata ai Preferiti.\n\nContinuare?\n");
        alertDialog.setPositiveButton("Aggiungi ai preferiti", (dialog, which) -> {
            presenter.onDoubleAdditionConfirmation();
            dialog.dismiss();
        });
        alertDialog.setNegativeButton("Cancella", (dialog, which) -> dialog.dismiss());
        alertDialog.create().show();
    }

    @Override
    public void showErrorMessage(String tvMessage, String btnMessage, ENUM_ERROR_BTN_STATUS intent) {
        progressBar.setVisibility(View.GONE);
        rvTrainDetails.setVisibility(View.GONE);
        rlEmptyJourneyBox.setVisibility(View.VISIBLE);
        btnErrorMessage.setText(btnMessage);
        tvErrorMessage.setText(tvMessage);
        btnErrorMessage.setOnClickListener(v -> {
            Intent i;
            switch (intent) {
                case CONN_SETTINGS:
                    analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ERROR_CONNECTIVITY);
                    Log.d("intent a settings");
                    i = new Intent(Settings.ACTION_SETTINGS);
                    startActivity(i);
                    break;
                case SEND_REPORT:
                    analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ERROR_SERVER);
                    FirebaseCrash.report(new Exception(SCREEN_SOLUTION_DETAILS + ERROR_SERVER + ": " +
                            "" + presenter.getSolution().getDepartureStationId() +
                            " > " + presenter.getSolution().getArrivalStationId() +
                            " @ " + presenter.getSolution().getDepartureTimeReadable() +
                            " currTime " + DateTime.now().toString("HH:mm")));
                    showSnackbar("Messaggio ricevuto! Grazie per l'aiuto!", NONE, Snackbar.LENGTH_LONG);
                    Log.d("intent a report");
                    break;
                case NO_SOLUTIONS:
                    analyticsHelper.logScreenEvent(SCREEN_SOLUTION_DETAILS, ERROR_NOT_FOUND_SOLUTION);
                    Log.d("intent a ricerca");
                    finish();
                    break;
            }
        });
    }

    @Override
    public void onQueryInventoryFinished(IabResult result, Inventory inv) {
        if (ignoreBeingProAndShowAds) {
            loadAds();
        } else {
            if (CustomIABHelper.isOrderOk(result, inv)) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    ((AppBarLayout) toolbar.getParent()).setElevation(10f);
                }
                Ads.removeAds(rlBannerPlaceholder, adView);
            } else {
                loadAds();
            }
        }
    }

    private void loadAds() {
        Ads.initializeAds(getViewContext(), rlBannerPlaceholder, adView, analyticsHelper, SCREEN_SOLUTION_DETAILS);
        BOTTOM_MARGIN_ACTUAL = BOTTOM_MARGIN_WITH_ADS;
    }
} public class AnimationUtils {

    public enum ANIM_TYPE {
        LIGHT(1.05f), MEDIUM(1.2f), HEAVY(1.2f);

        private float scalingOffset;

        ANIM_TYPE(float scalingOffset) {
            this.scalingOffset = scalingOffset;
        }

        public float getScalingOffset() {
            return scalingOffset;
        }
    }

    private final static float SCALING_OFFSET = 1.2f;

    public static void animOnPress(View v, ANIM_TYPE animType) {
        v.setScaleX(animType.getScalingOffset());
        v.setScaleY(animType.getScalingOffset());
        v.animate().setInterpolator(new OvershootInterpolator()).scaleX(1).scaleY(1).setDuration(250);
    }

    public static void onCompare(View v) {
        v.setScaleX(0f);
        v.setScaleY(0f);
        v.animate().setInterpolator(new OvershootInterpolator()).scaleX(1).scaleY(1).setDuration(150);
    }

} public class ViewsUtils {

    public static final ButterKnife.Action<View> VISIBLE = (view, index) -> view.setVisibility(View.VISIBLE);

    public static final ButterKnife.Action<View> GONE = (view, index) -> view.setVisibility(View.GONE);

    public static final ButterKnife.Action<View> INVISIBLE = (view, index) -> view.setVisibility(View.INVISIBLE);



    public enum COLORS {
        WHITE, YELLOW, ORANGE, RED, GREEN, BLACK, GREY_LIGHTER, BLUE
    }

    public static int getTimeDifferenceColor(Context context, COLORS color) {
        switch (color) {
            case WHITE:
                return getColor(context, R.color.txt_white);
            case YELLOW:
                return getColor(context, R.color.txt_yellow);
            case ORANGE:
                return getColor(context, R.color.txt_orange);
            case RED:
                return getColor(context, R.color.txt_red);
            case GREEN:
                return getColor(context, R.color.txt_green);
            case BLACK:
                return getColor(context, R.color.txt_dark);
            case GREY_LIGHTER:
                return getColor(context, R.color.txt_grey_lighter);
            case BLUE:
                return getColor(context, R.color.btn_dark_cyan);
            default:
                return getColor(context, R.color.txt_dark);
        }
    }

    public static int getTimeDifferenceColor(Context context, int timeDifference) {
        int color = getColor(context, R.color.txt_dark);
        if (timeDifference == 0) {
            color = getColor(context, R.color.ontime);
        } else if (timeDifference > 0) {
            color = getColor(context, R.color.late1);
        } else if (timeDifference < 0) {
            color = getColor(context, R.color.early1);
        }
        return color;
    }

    public static int getColor(Context context, int resource) {
        return ContextCompat.getColor(context, resource);
    }

    public static int convertDPtoPX(Context context, float dp) {
        float d = context.getResources().getDisplayMetrics().density;
        return (int) (dp * d); // margin in pixels
    }

} public class AnalyticsHelper {

    /*

    adb shell setprop log.tag.FA VERBOSE
    adb shell setprop log.tag.FA-SVC VERBOSE
    adb logcat -v time -s FA FA-SVC

     */

    private FirebaseAnalytics firebase = null;

    private static AnalyticsHelper analyticsHelper;

    public static AnalyticsHelper getInstance(Context context) {
        if (analyticsHelper == null) {
            analyticsHelper = new AnalyticsHelper(context);
        }
        return analyticsHelper;
    }

    private AnalyticsHelper(Context context) {
        firebase = FirebaseAnalytics.getInstance(context);
        firebase.setMinimumSessionDuration(3000);
//        firebase.setUserId();
//        firebase.setUserProperty();
    }

    public void logScreenEvent(String screen, String action) {
        Bundle bundle = new Bundle();
        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, screen);
        bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, action);
        bundle.putString(FirebaseAnalytics.Param.ITEM_ID, action);
        firebase.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
    }
} public static final ButterKnife.Action<View> GONE = (view, index) -> view.setVisibility(View.GONE); public static final ButterKnife.Action<View> INVISIBLE = (view, index) -> view.setVisibility(View.INVISIBLE); public static final ButterKnife.Action<View> VISIBLE = (view, index) -> view.setVisibility(View.VISIBLE); public static final String ACTION_REFRESH_DELAY_ALREADY         = "Refresh delay already"; public static final String ACTION_REFRESH_DELAY_FIRST           = "Refresh delay first"; public static final String ACTION_SEARCH_MORE_AFTER             = "Search more after"; public static final String ACTION_SEARCH_MORE_BEFORE            = "Search more before"; public static final String ACTION_SET_NOTIFICATION_FROM_RESULTS = "set notification from results"; public static final String ACTION_SOLUTION_CLICK                = "Click solution"; public static final String SCREEN_JOURNEY_RESULTS    = "RESJ"; public static final String I_SOLUTION      = "solution"; public static final String I_STATIONS     = "stations";
import com.google.firebase.crash.FirebaseCrash;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.jaus.albertogiunta.justintrain_oraritreni.R;
import com.jaus.albertogiunta.justintrain_oraritreni.data.Journey;
import com.jaus.albertogiunta.justintrain_oraritreni.networking.DateTimeAdapter;
import com.jaus.albertogiunta.justintrain_oraritreni.networking.PostProcessingEnabler;
import com.jaus.albertogiunta.justintrain_oraritreni.trainDetails.TrainDetailsActivity;
import com.jaus.albertogiunta.justintrain_oraritreni.utils.components.AnimationUtils;
import com.jaus.albertogiunta.justintrain_oraritreni.utils.components.ViewsUtils;
import com.jaus.albertogiunta.justintrain_oraritreni.utils.helpers.AnalyticsHelper;
import org.joda.time.DateTime;
import java.util.LinkedList;
import java.util.List;
import butterknife.BindView;
import butterknife.BindViews;
import butterknife.ButterKnife;
import static butterknife.ButterKnife.apply;
import static butterknife.ButterKnife.bind;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.components.ViewsUtils.GONE;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.components.ViewsUtils.INVISIBLE;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.components.ViewsUtils.VISIBLE;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_REFRESH_DELAY_ALREADY;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_REFRESH_DELAY_FIRST;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_SEARCH_MORE_AFTER;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_SEARCH_MORE_BEFORE;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_SET_NOTIFICATION_FROM_RESULTS;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.ACTION_SOLUTION_CLICK;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_ANALYTICS.SCREEN_JOURNEY_RESULTS;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_INTENT.I_SOLUTION;
import static com.jaus.albertogiunta.justintrain_oraritreni.utils.constants.CONST_INTENT.I_STATIONS;
        TextView tvTrainCategory;
        @BindView(R.id.tv_train_id)
        TextView tvTrainNumber;

        @BindView(R.id.tv_changes_number)
        TextView tvChangesNumber;
        @BindView(R.id.tv_changes_text)
        TextView tvChangesText;

        @BindView(R.id.tv_departure_time)
        TextView tvDepartureTime;
        @BindView(R.id.tv_departure_time_with_delay)
        TextView tvDepartureTimeWithDelay;

        @BindView(R.id.tv_arrival_time)
        TextView tvArrivalTime;
        @BindView(R.id.tv_arrival_time_with_delay)
        TextView tvArrivalTimeWithDelay;

        @BindView(R.id.tv_time_difference)
        TextView    tvTimeDifference;
        @BindView(R.id.btn_refresh_journey)
        ImageButton btnRefreshJourney;
        @BindView(R.id.iv_suppressed)
        ImageView   ivSuppressed;

        ///

        @BindView(R.id.tv_lasting_time)
        TextView tvLastingTime;

        @BindView(R.id.rl_platform)
        RelativeLayout rlPlatform;
        @BindView(R.id.tv_platform)
        TextView       tvPlatform;

        @BindView(R.id.tv_change)
        TextView tvChange;

        @BindView(R.id.ic_bolt)
        ImageView icBolt;
        @BindView(R.id.ic_warning)
        ImageView icWarning;
        @BindView(R.id.ic_coincidence_to_be_missed)
        ImageView icCoincidenceToBeMissed;

        @BindView(R.id.ll_notification)
        LinearLayout llNotification;

        @BindView(R.id.btn_pin)
        ImageView btnPin;

        @BindViews({R.id.ll_changes_number, R.id.rl_changes})
        List<View> viewsForSolutionWithChanges;

        @BindViews({R.id.ll_train_identification, R.id.rl_platform})
        List<View> viewsForDirectSolution;

        @BindViews({R.id.tv_time_difference, R.id.tv_arrival_time_with_delay, R.id.tv_departure_time_with_delay})
        List<View> viewsForTimeDifference;

        @BindViews({R.id.btn_refresh_journey})
        List<View> viewsForRefreshableSolution;

        @BindViews({R.id.tv_arrival_time_with_delay, R.id.tv_departure_time_with_delay})
        List<View> timesWithDelay;

        @BindViews({R.id.iv_suppressed, R.id.tv_suppressed})
        List<View> viewsForSuppressedSolution;

        @BindViews({R.id.tv_time_difference, R.id.tv_arrival_time_with_delay, R.id.tv_departure_time_with_delay})
        List<View> viewsForFilledNotSuppressedSolution;

        boolean isFilled, isSuppressed, hasAlert, isOnTime, hasEmptyRightSide, coincidenceToBeMissed;

        JourneyHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

        void bind(Journey.Solution solution) {
            isFilled = solution.getTimeDifference() != null;
            isSuppressed = !solution.hasChanges() && solution.getTrainStatusCode() != null && solution.getTrainStatusCode() == 2;
            hasAlert = !solution.hasChanges() && solution.getTrainStatusCode() != null && solution.getTrainStatusCode() != 1 && solution.getTrainStatusCode() != 2;
            isOnTime = solution.getTimeDifference() != null && solution.getTimeDifference() <= 0;
            hasEmptyRightSide = false;
            coincidenceToBeMissed = false;

            if (solution.getDepartureTime() != null &&
                    (solution.getDepartureTime().isAfter(DateTime.now().withHourOfDay(23).withMinuteOfHour(59)) ||
                    solution.getArrivalTime().isBefore(DateTime.now().withHourOfDay(0).withMinuteOfHour(0)))) {
                if (solution.getDepartureTime().getHourOfDay() > 3 ||
                        DateTime.now().getHourOfDay() < 22) {
                    hasEmptyRightSide = true;
                }
            }

            if (solution.hasChanges()) {
                try {
                    for (int i = 0; i < solution.getChangesList().size() - 1; i++) {
                        Journey.Solution.Change first  = solution.getChangesList().get(i);
                        Journey.Solution.Change second = solution.getChangesList().get(i + 1);

                        int firstTimeDifference  = first.getTimeDifference() != null && first.getTimeDifference() > 0 ? first.getTimeDifference() : 0;
                        int secondTimeDifference = second.getTimeDifference() != null && first.getTimeDifference() > 0 ? second.getTimeDifference() : 0;

                        if (first.getArrivalTime().plusMinutes(firstTimeDifference).isAfter(second.getDepartureTime().plusMinutes(secondTimeDifference))) {
                            coincidenceToBeMissed = true;
                            break;
                        }
                    }
                } catch (Exception e) {
                    coincidenceToBeMissed = false;
                    FirebaseCrash.report(new Exception("Got exception while checking coincidences in JOURNEYRESULTSADAPTER. The exception message is " + e.getMessage()));
                }
            }

            cvJourney.setOnClickListener(v -> {
                AnalyticsHelper.getInstance(context).logScreenEvent(SCREEN_JOURNEY_RESULTS, ACTION_SOLUTION_CLICK);
                Gson gson = new GsonBuilder()
                        .registerTypeAdapter(DateTime.class, new DateTimeAdapter())